diff options
author | Roman / Linnea Gräf <roman.graef@gmail.com> | 2023-03-13 23:43:40 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-13 23:43:40 +0100 |
commit | 86cd165c1ad9a72567cf5d033a8ff92779f72b30 (patch) | |
tree | 55960f96bd155aaa29ca03c5f79e61f830aa0ae0 /src/main/kotlin/io | |
parent | d249bbdc8e99bfdab81aa6b215e70c4f21def91e (diff) | |
download | NotEnoughUpdates-86cd165c1ad9a72567cf5d033a8ff92779f72b30.tar.gz NotEnoughUpdates-86cd165c1ad9a72567cf5d033a8ff92779f72b30.tar.bz2 NotEnoughUpdates-86cd165c1ad9a72567cf5d033a8ff92779f72b30.zip |
Improve NPC shop generator (#650)
Diffstat (limited to 'src/main/kotlin/io')
8 files changed, 645 insertions, 0 deletions
diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/generators/ItemSearchGui.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/generators/ItemSearchGui.kt new file mode 100644 index 00000000..2af7975e --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/generators/ItemSearchGui.kt @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2023 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.recipes.generators + +import io.github.moulberry.notenoughupdates.NotEnoughUpdates +import io.github.moulberry.notenoughupdates.util.Utils +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.GuiScreen +import net.minecraft.client.gui.GuiTextField +import net.minecraft.client.renderer.Tessellator +import net.minecraft.init.Items +import net.minecraft.item.ItemStack +import net.minecraftforge.fml.client.GuiScrollingList + +class ItemSearchGui(initialText: String, val onSelect: (String?) -> Unit) : GuiScreen() { + val textField = GuiTextField( + 0, Minecraft.getMinecraft().fontRendererObj, + -1, + 5, + 200, + 20 + ) + var lastFilter = "" + + class ItemScrollingList( + screenWidth: Int, + screenHeight: Int, + val callback: (ItemStack) -> Unit + ) : GuiScrollingList( + Minecraft.getMinecraft(), + 200, + screenHeight - 30, + 30, + screenHeight, + screenWidth / 2 - 100, + 20, + screenWidth, + screenHeight + ) { + var items: List<ItemStack> = listOf() + private set + var selected: Int? = null + + fun setItems(newItems: List<ItemStack>) { + this.items = newItems + selected = null + } + + override fun getSize(): Int { + return items.size + } + + override fun elementClicked(i: Int, doubleClick: Boolean) { + if (doubleClick) + callback(items[i]) + selected = i + } + + override fun isSelected(i: Int): Boolean { + return selected == i + } + + override fun drawBackground() { + + } + + override fun drawSlot(i: Int, right: Int, top: Int, height: Int, tessellator: Tessellator?) { + val item = items[i] + Utils.drawItemStack(item, this.left + 1, top + 1) + Utils.drawStringF(item.displayName, this.left + 18F, top + 1F, true, 0xFF00FF00.toInt()) + } + } + + lateinit var items: ItemScrollingList + + + init { + textField.text = initialText + } + + override fun initGui() { + super.initGui() + textField.xPosition = width / 2 - 100 + textField.setCanLoseFocus(false) + textField.isFocused = true + items = ItemScrollingList(width, height) { + val name = NotEnoughUpdates.INSTANCE.manager.createItemResolutionQuery() + .withItemStack(it) + .resolveInternalName() ?: return@ItemScrollingList + onSelect(name) + } + updateItems() + } + + override fun drawScreen(mouseX: Int, mouseY: Int, partialTicks: Float) { + super.drawScreen(mouseX, mouseY, partialTicks) + drawDefaultBackground() + textField.drawTextBox() + items.drawScreen(mouseX, mouseY, partialTicks) + } + + override fun onGuiClosed() { + onSelect(null) + } + override fun mouseClicked(mouseX: Int, mouseY: Int, mouseButton: Int) { + super.mouseClicked(mouseX, mouseY, mouseButton) + textField.mouseClicked(mouseX, mouseY, mouseButton) + } + + override fun keyTyped(typedChar: Char, keyCode: Int) { + super.keyTyped(typedChar, keyCode) + textField.textboxKeyTyped(typedChar, keyCode) + } + + fun updateItems() { + lastFilter = textField.text + + val candidates = + NotEnoughUpdates.INSTANCE.manager.search(textField.text) + .map { + NotEnoughUpdates.INSTANCE.manager.createItemResolutionQuery() + .withKnownInternalName(it) + .resolveToItemStack() ?: ItemStack( + Items.painting, 1, 10 + ) + } + items.setItems(candidates) + } + + override fun updateScreen() { + super.updateScreen() + textField.updateCursorCounter() + if (textField.text != lastFilter) updateItems() + } +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/generators/ItemShopExporter.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/generators/ItemShopExporter.kt new file mode 100644 index 00000000..e2673125 --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/generators/ItemShopExporter.kt @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2023 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.recipes.generators + +import com.google.auto.service.AutoService +import io.github.moulberry.notenoughupdates.recipes.Ingredient +import io.github.moulberry.notenoughupdates.recipes.ItemShopRecipe +import io.github.moulberry.notenoughupdates.util.ItemUtils +import io.github.moulberry.notenoughupdates.util.JsonUtils +import io.github.moulberry.notenoughupdates.util.SBInfo +import io.github.moulberry.notenoughupdates.util.Utils +import io.github.moulberry.notenoughupdates.util.kotlin.set +import net.minecraft.client.gui.GuiScreen +import net.minecraft.client.gui.inventory.GuiChest +import net.minecraft.inventory.ContainerChest +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.StringUtils + +@AutoService(RepoExporter::class) +class ItemShopExporter : RepoExporter { + override suspend fun export(context: RepoExportingContext) { + val chest = context.gui as GuiChest + val container = chest.inventorySlots as ContainerChest + val inventory = container.lowerChestInventory + val displayName = inventory.displayName.unformattedText + val npcInternalName = displayName.uppercase().replace(" ", "_") + "_NPC" + val file = context.manager.repoLocation.resolve("items/$npcInternalName.json") + val itemDisplayName = "§9$displayName (NPC)" + val baseNPCJson = context.manager.createItemJson( + npcInternalName, + "minecraft:skull", + itemDisplayName, + arrayOf(""), + null, + "WIKI_URL", + arrayOf("TODO"), + "viewrecipe", + 3, + NBTTagCompound() + ) + baseNPCJson["x"] = context.mc.thePlayer.posX.toInt() + baseNPCJson["y"] = context.mc.thePlayer.posY.toInt() + baseNPCJson["z"] = context.mc.thePlayer.posZ.toInt() + baseNPCJson["island"] = SBInfo.getInstance().getLocation() + + val recipes = mutableListOf<ItemShopRecipe>() + for (slotNum in 0 until inventory.sizeInventory) { + val slot = inventory.getStackInSlot(slotNum) ?: continue + if (slot.displayName.isNullOrBlank()) continue + + recipes.add(findRecipe(context, slot) ?: continue) + } + baseNPCJson["recipes"] = JsonUtils.transformListToJsonArray(recipes, ItemShopRecipe::serialize) + context.writeFile(file, baseNPCJson) + } + + val coinRegex = "^([0-9,]+) Coins$".toRegex() + val itemRegex = "^(?<name>.*?)(?: x(?<amount>[0-9,]+))?$".toRegex() + suspend fun findRecipe( + context: RepoExportingContext, + stack: ItemStack, + ): ItemShopRecipe? { + val resultName = + context.manager.createItemResolutionQuery().withItemStack(stack).resolveInternalName() ?: return null + + var inCost = false + val costList = mutableListOf<Ingredient>() + for (loreLine in ItemUtils.getLore(stack)) { + var loreLine = loreLine + if (loreLine == "§7Cost") { + inCost = true + continue + } else if (loreLine == "§eClick to trade!") { + inCost = false + continue + } else if (loreLine.startsWith("§7Cost: ")) { + loreLine = loreLine.substringAfter("§7Cost: ") + inCost = true + } else if (!inCost) continue + val cleanLine = StringUtils.stripControlCodes(loreLine) + if (cleanLine.isBlank()) continue + val ingredient = findIngredientForLoreLine(context, cleanLine) + if (ingredient == null) { + Utils.addChatMessage("Warning: Could not find ingredient for $loreLine") + } else { + costList.add(ingredient) + } + } + return ItemShopRecipe( + null, costList, Ingredient(context.manager, resultName, stack.stackSize.toDouble()), null + ) + } + + suspend fun findIngredientForLoreLine( + context: RepoExportingContext, + loreLine: String, + ): Ingredient? { + val coinMatch = coinRegex.matchEntire(loreLine) + if (coinMatch != null) { + val amount = coinMatch.groupValues[1].replace(",", "").toInt() + return Ingredient.coinIngredient(context.manager, amount) + } + val itemMatch = itemRegex.matchEntire(loreLine) + if (itemMatch != null) { + val label = itemMatch.groups["name"]!!.value + val amount = itemMatch.groups["amount"]?.value?.replace(",", "")?.toDouble() ?: 1.0 + if (context.manager.itemInformation.containsKey(label.uppercase())) { + return Ingredient(context.manager, label, amount) + } + return Ingredient(context.manager, context.findItemByName(label), amount) + } + return null + } + + override fun canExport(gui: GuiScreen): Boolean { + return gui is GuiChest + } + + override val name: String + get() = "Item Shop" +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/generators/RepoExporter.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/generators/RepoExporter.kt new file mode 100644 index 00000000..c03bb498 --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/generators/RepoExporter.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2023 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.recipes.generators + +import net.minecraft.client.gui.GuiScreen + +interface RepoExporter { + suspend fun export(context: RepoExportingContext) + fun canExport(gui: GuiScreen): Boolean + val name: String +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/generators/RepoExporters.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/generators/RepoExporters.kt new file mode 100644 index 00000000..1ffbd696 --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/generators/RepoExporters.kt @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2023 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.recipes.generators + +import io.github.moulberry.notenoughupdates.NotEnoughUpdates +import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe +import io.github.moulberry.notenoughupdates.events.GuiContainerBackgroundDrawnEvent +import io.github.moulberry.notenoughupdates.mixins.AccessorGuiContainer +import io.github.moulberry.notenoughupdates.util.Utils +import io.github.moulberry.notenoughupdates.util.kotlin.Coroutines +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.GuiButton +import net.minecraft.client.gui.inventory.GuiContainer +import net.minecraftforge.client.event.GuiScreenEvent +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import org.lwjgl.input.Mouse +import java.util.* + +@NEUAutoSubscribe +object RepoExporters { + private val allRepoExporters = ServiceLoader.load(RepoExporter::class.java).let { + it.reload() + it.toList() + } + private var lastRenderedButtons = listOf<Pair<GuiButton, RepoExporter>>() + private var lastGui: GuiContainer? = null + + @SubscribeEvent + fun onGuiRender(event: GuiContainerBackgroundDrawnEvent) { + if (!NotEnoughUpdates.INSTANCE.config.apiData.repositoryEditing) return + val mouseX = Utils.getMouseX() + val mouseY = Utils.getMouseY() + val gui = event.container + if (gui !is AccessorGuiContainer) return + val exporters = allRepoExporters.filter { it.canExport(gui) } + synchronized(this) { + lastGui = gui + lastRenderedButtons = exporters.withIndex().map { (index, exporter) -> + GuiButton( + 0, + 0, -30 - 20 * index, + gui.xSize, 20, + "Run Exporter: ${exporter.name}" + ).also { + it.drawButton( + Minecraft.getMinecraft(), + mouseX - gui.guiLeft, + mouseY - gui.guiTop + ) + } to exporter + } + } + } + + @SubscribeEvent + fun onGuiClick(event: GuiScreenEvent.MouseInputEvent.Pre) { + if (!Mouse.getEventButtonState()) return + val accessor = event.gui as? AccessorGuiContainer ?: return + + val mouseX = Utils.getMouseX() - accessor.guiLeft + val mouseY = Utils.getMouseY() - accessor.guiTop + + val exporter = synchronized(this) { + if (lastGui !== event.gui) return + lastRenderedButtons.forEach { (button, exporter) -> + if (button.mousePressed(Minecraft.getMinecraft(), mouseX, mouseY)) { + return@synchronized exporter + } + } + return + } + event.isCanceled = true + Coroutines.launchCoroutineOnCurrentThread { + try { + exporter.export(RepoExportingContext(NotEnoughUpdates.INSTANCE.manager, event.gui)) + Utils.addChatMessage("Repo export completed") + } catch (e: RepoExportingInterruptedException) { + Utils.addChatMessage("Repo exporting interrupted") + } + } + } +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/generators/RepoExportingContext.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/generators/RepoExportingContext.kt new file mode 100644 index 00000000..66381242 --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/generators/RepoExportingContext.kt @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2023 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.recipes.generators + +import com.google.gson.JsonObject +import io.github.moulberry.notenoughupdates.NEUManager +import io.github.moulberry.notenoughupdates.util.MinecraftExecutor +import io.github.moulberry.notenoughupdates.util.kotlin.Coroutines.continueOn +import io.github.moulberry.notenoughupdates.util.kotlin.Coroutines.waitTicks +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.GuiScreen +import net.minecraft.client.gui.GuiYesNo +import java.io.File +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine + +class RepoExportingContext( + val manager: NEUManager, + val gui: GuiScreen, +) { + val mc = Minecraft.getMinecraft() + val nameToItemCache = mutableMapOf<String, String>() + + suspend fun writeFile(file: File, json: JsonObject) { + if (file.exists()) { + if (!askYesNo("Overwrite file?", "The file $file already exists.")) + return + } + manager.writeJson(json, file) + } + + suspend fun askYesNo(title: String, subtitle: String): Boolean { + var wasResolved = false + continueOn(MinecraftExecutor.OnThread) + val result = suspendCoroutine { + mc.displayGuiScreen(object : GuiYesNo({ result, id -> + if (!wasResolved) { + wasResolved = true + it.resume(result) + } + }, title, subtitle, 0) { + override fun onGuiClosed() { + if (!wasResolved) { + wasResolved = true + it.resume(null) + } + } + }) + } + if (result == null) { + waitTicks(1) // Wait one tick for gui closing animation + } + mc.displayGuiScreen(gui) + return result ?: false + } + + suspend fun findItemByName(name: String): String { + val c = nameToItemCache[name] + if (c != null) return c + var wasResolved = false + continueOn(MinecraftExecutor.OnThread) + val result = suspendCoroutine { cont -> + mc.displayGuiScreen(ItemSearchGui(name) { + if (!wasResolved) { + wasResolved = true + cont.resume(it) + } + }) + } + waitTicks(1) + mc.displayGuiScreen(gui) + result ?: throw RepoExportingInterruptedException() + nameToItemCache[name] = result + return result + } +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/generators/RepoExportingInterruptedException.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/generators/RepoExportingInterruptedException.kt new file mode 100644 index 00000000..9839f200 --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/generators/RepoExportingInterruptedException.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.recipes.generators + +class RepoExportingInterruptedException : Error() { + +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/kotlin/Coroutines.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/kotlin/Coroutines.kt new file mode 100644 index 00000000..4bcf0c61 --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/kotlin/Coroutines.kt @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2023 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.util.kotlin + +import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.fml.common.gameevent.TickEvent +import java.util.concurrent.CompletableFuture +import java.util.concurrent.Executor +import kotlin.coroutines.* + +@NEUAutoSubscribe +object Coroutines { + fun <T> launchCoroutineOnCurrentThread(block: suspend () -> T): CompletableFuture<T> { + val future = CompletableFuture<T>() + val coroutine = block.createCoroutine(object : Continuation<T> { + override val context: CoroutineContext + get() = EmptyCoroutineContext + + override fun resumeWith(result: Result<T>) { + try { + future.complete(result.getOrThrow()) + } catch (ex: Throwable) { + future.completeExceptionally(ex) + } + } + }) + coroutine.resume(Unit) + return future + } + + suspend fun continueOn(ex: Executor) { + suspendCoroutine { + ex.execute { + it.resume(Unit) + } + } + } + + private data class DelayedTask(val contination: () -> Unit, var tickDelay: Int) + + private val tasks = mutableListOf<DelayedTask>() + + @SubscribeEvent + fun onTick(event: TickEvent.ClientTickEvent) { + if (event.phase == TickEvent.Phase.END) { + val toRun = mutableListOf<DelayedTask>() + synchronized(tasks) { + tasks.removeIf { + it.tickDelay-- + if (it.tickDelay <= 0) { + toRun.add(it) + return@removeIf true + } + false + } + } + toRun.forEach { it.contination() } + } + } + + suspend fun waitTicks(tickCount: Int) { + suspendCoroutine { + synchronized(tasks) { + tasks.add(DelayedTask({ it.resume(Unit) }, tickCount)) + } + } + } + + +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/kotlin/gson.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/kotlin/gson.kt index 00c3d729..ec2e3c31 100644 --- a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/kotlin/gson.kt +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/kotlin/gson.kt @@ -20,6 +20,8 @@ package io.github.moulberry.notenoughupdates.util.kotlin import com.google.gson.Gson +import com.google.gson.JsonElement +import com.google.gson.JsonObject import com.google.gson.reflect.TypeToken import java.lang.reflect.Type @@ -31,3 +33,23 @@ inline fun <reified T : Any> typeToken(): Type { inline fun <reified T : Any> Gson.fromJson(string: String): T { return fromJson(string, typeToken<T>()) } + +operator fun JsonObject.set(name: String, value: Number) { + this.addProperty(name, value) +} + +operator fun JsonObject.set(name: String, value: String) { + this.addProperty(name, value) +} + +operator fun JsonObject.set(name: String, value: Boolean) { + this.addProperty(name, value) +} + +operator fun JsonObject.set(name: String, value: Char) { + this.addProperty(name, value) +} + +operator fun JsonObject.set(name: String, value: JsonElement) { + this.add(name, value) +} |