Skip to content

Commit

Permalink
Fixed memory leaks & bug fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
ToberoCat committed Sep 29, 2023
1 parent 51cacf6 commit e4e7956
Show file tree
Hide file tree
Showing 9 changed files with 94 additions and 25 deletions.
13 changes: 9 additions & 4 deletions src/main/kotlin/io/github/toberocat/guiengine/GuiEngineApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import io.github.toberocat.guiengine.interpreter.InterpreterManager
import io.github.toberocat.guiengine.utils.FileUtils
import io.github.toberocat.guiengine.utils.VirtualInventory
import io.github.toberocat.guiengine.utils.VirtualPlayer
import io.github.toberocat.guiengine.utils.VirtualView
import io.github.toberocat.guiengine.utils.logger.GuiLogger
import io.github.toberocat.guiengine.view.DefaultGuiViewManager
import io.github.toberocat.guiengine.xml.GuiComponentDeserializer
Expand All @@ -23,6 +24,7 @@ import io.github.toberocat.guiengine.xml.XmlGui
import org.apache.commons.text.StringSubstitutor
import org.bukkit.Bukkit
import org.bukkit.entity.Player
import org.bukkit.event.inventory.InventoryCloseEvent
import org.bukkit.plugin.java.JavaPlugin
import java.io.File
import java.io.FileFilter
Expand Down Expand Up @@ -77,7 +79,7 @@ class GuiEngineApi(
xmlMapper.registerModules(SHARED_MODULES)
xmlMapper.registerModule(kotlinModule())
xmlMapper.factory.xmlTextElementName = "$"

this.id = GUI_ID_REGEX.matcher(id).replaceAll("")
when {
!guiFolder.exists() && !guiFolder.mkdirs() -> Bukkit.getLogger()
Expand Down Expand Up @@ -248,10 +250,11 @@ class GuiEngineApi(
total += delta
now = System.currentTimeMillis()
val renderTask = CompletableFuture.supplyAsync<Exception?> {

val virtualInventory = VirtualInventory(interpreter.renderEngine.height(context)) {}
val virtualBuffer = interpreter.renderEngine.createBuffer(context)
context.setViewer(virtualPlayer)
context.setInventory(VirtualInventory(interpreter.renderEngine.height(context)) {})
context.setInventory(virtualInventory)

try {
context.componentsDescending().forEach { it?.onViewInit(HashMap()) }
interpreter.renderEngine.renderGui(virtualBuffer, context, virtualPlayer)
Expand All @@ -260,6 +263,7 @@ class GuiEngineApi(
}
null
}

try {
val exception = renderTask[1, TimeUnit.SECONDS]
if (null != exception) throw exception
Expand All @@ -276,6 +280,7 @@ class GuiEngineApi(
}
total += delta
logger.debug("§aTook in total ${delta}ms§a to get $gui displayed to the virtual player")
context.closedComponent(InventoryCloseEvent(VirtualView(virtualPlayer)))
} catch (e: InvalidGuiComponentException) {
availableGuis.remove(gui)
logger.error(String.format("%s.gui has a invalid component. %s", gui, e.message))
Expand All @@ -285,7 +290,7 @@ class GuiEngineApi(
} catch (e: Throwable) {
availableGuis.remove(gui)
e.printStackTrace()
logger.error("The gui couldn't get rendered to an " + "virtual player. Please take a look at it")
logger.error("The gui couldn't get rendered to an virtual player. Please take a look at it")
throw GuiIORuntimeException(e)
}
return total
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ class GuiEngineApiPlugin : JavaPlugin() {
FunctionProcessor.registerFunction(RandomFunction.TYPE, RandomFunction.Deserializer())
FunctionProcessor.registerFunction(ActionFunction.TYPE, ActionFunction.Factory())
FunctionProcessor.registerFunction(DelayFunction.TYPE, DelayFunction.Deserializer())
FunctionProcessor.registerFunction(InputFunction.TYPE, InputFunction.Deserializer())
FunctionProcessor.registerFunction(InputFunction.TYPE, InputFunction.Factory())

FunctionProcessor.registerComputeFunction(GuiComponentPropertyFunction())
FunctionProcessor.registerComputeFunction(HasPermissionFunction())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ interface PagedContainer : LayoutContainer {
fun addPage(context: GuiContext)
fun addPage(context: GuiContext, position: Int)
fun createEmptyPage(): PatternPage?

fun showNext() = page != availablePages - 1
fun showPrevious() = page != 0
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import io.github.toberocat.guiengine.event.spigot.GuiComponentClickEvent
import io.github.toberocat.guiengine.event.spigot.GuiComponentDragEvent
import io.github.toberocat.guiengine.function.FunctionProcessor
import io.github.toberocat.guiengine.interpreter.GuiInterpreter
import io.github.toberocat.guiengine.utils.VirtualView
import io.github.toberocat.guiengine.xml.XmlComponent
import io.github.toberocat.guiengine.xml.XmlGui
import io.github.toberocat.toberocore.util.StreamUtils
Expand All @@ -41,7 +42,10 @@ import java.util.stream.Stream
* @since 04/02/2023
*/
open class GuiContext(
val interpreter: GuiInterpreter, val api: GuiEngineApi, val xmlGui: XmlGui, private val contextType: ContextType
val interpreter: GuiInterpreter,
val api: GuiEngineApi,
val xmlGui: XmlGui,
private val contextType: ContextType
) : GuiEvents, GuiEventListener {
val components: MutableList<GuiComponent>

Expand All @@ -60,17 +64,17 @@ open class GuiContext(
*
* @return The unique identifier.
*/
val contextId: UUID
val contextId: UUID = UUID.randomUUID()
val domEvents: GuiDomEvents
val computableFunctionProcessor: RenderComputableFunctionProcessor = RenderComputableFunctionProcessor()
var propagateClose = true
private var inventory: Inventory? = null
private var viewer: Player? = null

init {
GuiEngineApi.LOADED_CONTEXTS[contextId] = this
components = ArrayList()
contextId = UUID.randomUUID()
domEvents = GuiDomEvents(xmlGui)
GuiEngineApi.LOADED_CONTEXTS[contextId] = this
}

/**
Expand Down Expand Up @@ -123,6 +127,8 @@ open class GuiContext(
val xml = interpreter().componentToXml(component)
editCallback.accept(xml)
val newComponent = interpreter().createComponent(xml, api, this)

components[index].closedComponent(InventoryCloseEvent(VirtualView(viewer)))
components[index] = newComponent
}

Expand Down Expand Up @@ -198,6 +204,8 @@ open class GuiContext(
* @param event The `InventoryCloseEvent` representing the close event.
*/
override fun closedComponent(event: InventoryCloseEvent) {
if (!propagateClose)
return
componentsDescending().filter { x: GuiComponent? -> !x!!.hidden() }
.forEachOrdered { x: GuiComponent? -> x!!.closedComponent(event) }
Bukkit.getPluginManager().callEvent(GuiCloseEvent(this, event))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package io.github.toberocat.guiengine.function

import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.xml.XmlMapper
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import io.github.toberocat.guiengine.context.GuiContext
import io.github.toberocat.guiengine.exception.GuiFunctionException
import io.github.toberocat.guiengine.utils.Utils
Expand All @@ -24,7 +21,6 @@ object FunctionProcessor {
private val pattern = Pattern.compile("\\{([^}]*)}", Pattern.MULTILINE)
private val FUNCTIONS: MutableMap<String, GuiFunctionFactory<*>> = HashMap()
private val COMPUTE_FUNCTIONS: MutableList<GuiComputeFunction> = ArrayList()
private val OBJECT_MAPPER: ObjectMapper = XmlMapper().registerKotlinModule()

/**
* Registers a custom GUI function.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,39 @@
package io.github.toberocat.guiengine.function.call.input

import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.databind.JsonSerializer
import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import com.fasterxml.jackson.databind.annotation.JsonSerialize
import io.github.toberocat.guiengine.GuiEngineApiPlugin
import io.github.toberocat.guiengine.context.GuiContext
import io.github.toberocat.guiengine.exception.GuiFunctionException
import io.github.toberocat.guiengine.function.FunctionProcessor
import io.github.toberocat.guiengine.function.GuiFunction
import io.github.toberocat.guiengine.function.GuiFunctionFactory
import io.github.toberocat.guiengine.xml.parsing.GeneratorContext
import io.github.toberocat.guiengine.xml.parsing.ParserContext
import io.github.toberocat.guiengine.xml.parsing.PlaceholderParserContext
import io.github.toberocat.guiengine.xml.parsing.PlaceholderParserContext.Companion.toPlaceholderContext
import org.bukkit.Bukkit
import org.bukkit.entity.Player

@JsonDeserialize(using = InputFunction.Deserializer::class)
data class InputFunction(val inputType: InputType, val variable: String, val parser: ParserContext) : GuiFunction {
@JsonDeserialize(using = InputFunction.Factory::class)
@JsonSerialize(using = InputFunction.Serilaizer::class)
data class InputFunction(
val inputType: InputType,
val variable: String,
val parser: ParserContext
) :
GuiFunction {
override val type = TYPE

override fun call(context: GuiContext) {
context.viewer()?.let { viewer ->
Bukkit.getScheduler().runTask(GuiEngineApiPlugin.plugin, Runnable {
context.propagateClose = false
inputType.takeInput(viewer, parser) {
runBody(
viewer, context, it
)
runBody(viewer, context, it)
}
})
}
Expand All @@ -37,19 +47,38 @@ data class InputFunction(val inputType: InputType, val variable: String, val par
placeholders["%$variable%"] = input
val functions = parser.toPlaceholderContext(placeholders).functions("function")
.require { GuiFunctionException("Input function must have at least a single function") }

context.propagateClose = true
FunctionProcessor.callFunctions(functions, context)
Bukkit.getScheduler().runTask(GuiEngineApiPlugin.plugin, Runnable {
context.inventory()?.let { viewer.openInventory(it) }
context.render()
})
}

class Deserializer : GuiFunctionFactory<InputFunction>() {
override fun build(node: ParserContext) = InputFunction(
node.enum(InputType::class.java, "input-type").require(TYPE, javaClass),
node.string("variable").require(TYPE, javaClass),
node
)
class Factory : GuiFunctionFactory<InputFunction>() {
override fun build(node: ParserContext): InputFunction {
println(node.node)
return InputFunction(
node.enum(InputType::class.java, "input-type").require(TYPE, javaClass),
node.string("variable").require(TYPE, javaClass),
node
)
}
}

class Serilaizer : JsonSerializer<InputFunction>() {
override fun serialize(function: InputFunction, generator: JsonGenerator, p2: SerializerProvider?) {
val context = GeneratorContext(generator)
generator.writeStartObject()
context.writeStringField("input-type", function.inputType.name)
context.writeStringField("variable", function.variable)
context.writeStringField("type", function.type)
context.writeFunctionField("function", function.parser.functions("function").optional(emptyList()))
function.inputType.serialize(function.parser, context)
generator.writeEndObject()
}

}

companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.toberocat.guiengine.function.call.input

import io.github.toberocat.guiengine.GuiEngineApiPlugin
import io.github.toberocat.guiengine.xml.parsing.GeneratorContext
import io.github.toberocat.guiengine.xml.parsing.ParserContext
import io.github.toberocat.toberocore.input.ChatInput
import org.bukkit.entity.Player
Expand All @@ -13,7 +14,12 @@ enum class InputType {
val message = context.string("message").require("Chat", javaClass)
ChatInput.prompt(GuiEngineApiPlugin.plugin, player, message, callback)
}

override fun serialize(parser: ParserContext, context: GeneratorContext) {
context.writeStringField("message", parser.string("message").optional(""))
}
};

abstract fun serialize(parser: ParserContext, context: GeneratorContext)
abstract fun takeInput(player: Player, context: ParserContext, callback: Consumer<String>)
}
22 changes: 22 additions & 0 deletions src/main/kotlin/io/github/toberocat/guiengine/utils/VirtualView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.github.toberocat.guiengine.utils

import org.bukkit.entity.Player
import org.bukkit.event.inventory.InventoryType
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.InventoryView

class VirtualView(private val player: Player?) : InventoryView() {
override fun getTopInventory(): Inventory {
TODO("Not yet implemented")
}

override fun getBottomInventory(): Inventory {
TODO("Not yet implemented")
}

override fun getPlayer() = player ?: throw IllegalArgumentException("Virtualized view has no player")

override fun getType() = InventoryType.CHEST

override fun getTitle() = "Virtualized View"
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class GuiComponentDeserializer<C : GuiComponent, B : GuiComponentBuilder?>(priva
val api = GuiEngineApi.APIS[apiId]
?: throw GuiException("Couldn't parse component. Interpreter didn't specify '__:api:__' to a correct api id")
val context = GuiEngineApi.LOADED_CONTEXTS[contextId]
?: throw GuiException("Couldn't parse component. Interpreter didn't specify '__:context:__' to a correct context id")
?: throw GuiException("Couldn't parse component. Value received: $contextId. Interpreter didn't specify '__:ctx:__' to a correct context id")

val computables: MutableMap<String, String> = mutableMapOf()
builder!!.deserialize(ParserContext(node, computables, context, api))
Expand Down

0 comments on commit e4e7956

Please sign in to comment.