Merge branch 'mo-molang-mo-problems' into 'main'

Add ability to specify file paths for MoLang stores

See merge request cable-mc/cobblemon!1744
This commit is contained in:
Hiroku
2025-09-06 05:55:06 +00:00
committed by GitLab
4 changed files with 43 additions and 38 deletions
+1
View File
@@ -38,6 +38,7 @@
- Pokémon with the ability Volt Absorb are immune to lightning damage and receive Regeneration II for a short duration
- Ground type Pokémon are immune to lightning damage
- Added functionality to Everstone when held by a Pokémon; suppresses evolution notification and hides evolve button in summary interface.
- Added optional `filePath` parameter to MoLang functions to allow for global stores (ex. `../global/`). Path must end in a forward slash.
- Added new optional property `attachment_options` for most EmitterShapes to be attached to the locator/entities scale, rotation, and/or position. Position is true by default.
- Galarica Nut bushes now generate on beaches.
- Some Pokémon now pitch their bodies in the direction they're moving.
@@ -488,27 +488,20 @@ object MoLangFunctions {
}
}
map.put("spawn_npc") { params ->
val x = params.getInt(0)
val y = params.getInt(1)
val z = params.getInt(2)
val npcClass = params.getString(3)
val pos = BlockPos(x, y, z)
if (!Level.isInSpawnableBounds(pos)) {
return@put DoubleValue.ZERO
}
val npc = NPCEntity(world) ?: return@put DoubleValue.ZERO
npc.moveTo(pos, npc.yRot, npc.xRot)
npc.npc = NPCClasses.getByIdentifier(npcClass.asIdentifierDefaultingNamespace()) ?: return@put DoubleValue.ZERO
npc.initialize(1)
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
val npcClass = params.getStringOrNull(3)?.let { NPCClasses.getByIdentifier(it.asIdentifierDefaultingNamespace()) }
val level = params.getInt(4)
if(npcClass == null) return@put DoubleValue.ZERO
val npc = NPCEntity(world)
npc.moveTo(x, y, z, npc.yRot, npc.xRot)
npc.npc = npcClass
npc.initialize(level)
if (world.addFreshEntity(npc)) {
return@put npc.struct
} else {
return@put DoubleValue.ZERO
return@put npc.asMoLangValue()
}
return@put DoubleValue.ZERO
}
map.put("play_sound_on_server") { params ->
val sound = params.getString(0).asResource()
@@ -682,13 +675,13 @@ object MoLangFunctions {
map.put("party") { player.party().struct }
map.put("pc") { player.pc().struct }
map.put("has_permission") { params -> DoubleValue(Cobblemon.permissionValidator.hasPermission(player, params.getString(0), params.getIntOrNull(1) ?: 4)) }
map.put("data") { _ -> Cobblemon.molangData.load(player.uuid) }
map.put("save_data") { _ -> Cobblemon.molangData.save(player.uuid) }
map.put("data") { params -> Cobblemon.molangData.load(player.uuid, params.getStringOrNull(0)) }
map.put("save_data") { params -> Cobblemon.molangData.save(player.uuid, params.getStringOrNull(0)) }
map.put("in_battle") { DoubleValue(player.isInBattle()) }
map.put("battle") { player.getBattleState()?.first?.struct ?: DoubleValue.ZERO }
map.put("get_npc_data") { params ->
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
val data = Cobblemon.molangData.load(player.uuid)
val npcId = ((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID ?: params.getString(0)
val data = Cobblemon.molangData.load(player.uuid, params.getStringOrNull(1))
if (data.map.containsKey(npcId)) {
return@put data.map[npcId]!!
} else {
@@ -698,9 +691,9 @@ object MoLangFunctions {
}
}
map.put("get_npc_variable") { params ->
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
val npcId = ((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID ?: params.getString(0)
val variable = params.getString(1)
val data = Cobblemon.molangData.load(player.uuid)
val data = Cobblemon.molangData.load(player.uuid, params.getStringOrNull(2))
if (data.map.containsKey(npcId)) {
return@put (data.map[npcId] as VariableStruct).map[variable] ?: DoubleValue.ZERO
} else {
@@ -708,15 +701,16 @@ object MoLangFunctions {
}
}
map.put("set_npc_variable") { params ->
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
val npcId = ((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID ?: params.getString(0)
val variable = params.getString(1)
val value = params.get<MoValue>(2)
val saveAfterwards = params.getBooleanOrNull(3) != false
val data = Cobblemon.molangData.load(player.uuid)
val path = params.getStringOrNull(4)
val data = Cobblemon.molangData.load(player.uuid, path)
val npcData = data.map.getOrPut(npcId) { VariableStruct() } as VariableStruct
npcData.map[variable] = value
if (saveAfterwards) {
Cobblemon.molangData.save(player.uuid)
Cobblemon.molangData.save(player.uuid, path)
}
return@put DoubleValue.ONE
}
@@ -999,6 +993,9 @@ object MoLangFunctions {
return@put if (entity.isStandingOn(blockStrings, depth)) DoubleValue.ONE else DoubleValue.ZERO
}
map.put("discard") {
entity.discard()
}
if (entity is PosableEntity) {
map.put("play_animation") { params ->
val animation = params.getString(0)
@@ -2124,12 +2121,17 @@ object MoLangFunctions {
}
map.put("data") { params ->
val data = Cobblemon.molangData.load(UUID(0L, 0L))
return@put data
Cobblemon.molangData.load(
UUID(0L, 0L),
params.getStringOrNull(0)
)
}
map.put("save_data") {
Cobblemon.molangData.save(UUID(0L, 0L))
map.put("save_data") { params ->
Cobblemon.molangData.save(
UUID(0L, 0L),
params.getStringOrNull(index = 0)
)
return@put DoubleValue.ONE
}
@@ -13,5 +13,5 @@ import java.util.UUID
interface MoLangDataStoreFactory {
fun markDirty(uuid: UUID)
fun load(uuid: UUID) : VariableStruct
fun load(uuid: UUID, filePath: String? = null) : VariableStruct
}
@@ -20,6 +20,8 @@ import java.nio.file.Path
import java.util.*
object NbtMoLangDataStoreFactory : MoLangDataStoreFactory {
const val DEFAULT_PATH = "playermolangdata/"
const val DEFAULT_FOLDER_STRUCTURE = "%s/%s.dat"
lateinit var savePath: Path
val cache = mutableMapOf<UUID, VariableStruct>()
val dirty = mutableListOf<UUID>()
@@ -57,7 +59,7 @@ object NbtMoLangDataStoreFactory : MoLangDataStoreFactory {
dirty.add(uuid)
}
override fun load(uuid: UUID): VariableStruct {
override fun load(uuid: UUID, filePath: String?): VariableStruct {
return if (cache.contains(uuid))
cache[uuid]!!
else {
@@ -68,7 +70,7 @@ object NbtMoLangDataStoreFactory : MoLangDataStoreFactory {
return data
}
val nbt = NbtIo.readCompressed(file(uuid), NbtAccounter.unlimitedHeap())
val nbt = NbtIo.readCompressed(file(uuid, filePath ?: DEFAULT_PATH), NbtAccounter.unlimitedHeap())
// If it's not a VariableStruct then someone's fucked around and will subsequently find out
val data = readMoValueFromNBT(nbt) as VariableStruct
@@ -77,8 +79,8 @@ object NbtMoLangDataStoreFactory : MoLangDataStoreFactory {
}
}
fun save(uuid: UUID) {
val file = file(uuid)
fun save(uuid: UUID, filePath: String? = null) {
val file = file(uuid, filePath ?: DEFAULT_PATH)
val data = cache[uuid] ?: return
val nbt = writeMoValueToNBT(data)!! as CompoundTag
file.toFile().parentFile.mkdirs()
@@ -86,5 +88,5 @@ object NbtMoLangDataStoreFactory : MoLangDataStoreFactory {
dirty -= uuid
}
private fun file(uuid: UUID) = savePath.resolve("playermolangdata/${uuid.toString().substring(0, 2)}/$uuid.dat")
private fun file(uuid: UUID, filePath: String = DEFAULT_PATH) = savePath.resolve(filePath + DEFAULT_FOLDER_STRUCTURE.format(uuid.toString().substring(0, 2), uuid))
}