1
0
mirror of https://github.com/chylex/Hardcore-Ender-Expansion-2.git synced 2025-04-11 03:15:44 +02:00

Implement territory storage component and ticking system & store Token type

This commit is contained in:
chylex 2020-10-31 20:56:05 +01:00
parent 284779bb64
commit 86c0b6a0ec
10 changed files with 254 additions and 13 deletions

View File

@ -96,7 +96,7 @@ class ItemPortalToken(properties: Properties) : Item(properties){
val territory = getTerritoryType(stack) ?: return null
val index = with(stack.heeTag){
getIntegerOrNull(TERRITORY_INDEX_TAG) ?: TerritoryGlobalStorage.get().assignNewIndex(territory).also { putInt(TERRITORY_INDEX_TAG, it) }
getIntegerOrNull(TERRITORY_INDEX_TAG) ?: TerritoryGlobalStorage.get().assignNewIndex(territory, getTokenType(stack)).also { putInt(TERRITORY_INDEX_TAG, it) }
}
return TerritoryInstance(territory, index)
@ -112,7 +112,7 @@ class ItemPortalToken(properties: Properties) : Item(properties){
return super.onItemRightClick(world, player, hand)
}
val index = heldItem.heeTagOrNull?.getIntegerOrNull(TERRITORY_INDEX_TAG) ?: TerritoryGlobalStorage.get().assignNewIndex(territory)
val index = heldItem.heeTagOrNull?.getIntegerOrNull(TERRITORY_INDEX_TAG) ?: TerritoryGlobalStorage.get().assignNewIndex(territory, getTokenType(heldItem))
val instance = TerritoryInstance(territory, index)
if (!EntityPortalContact.shouldTeleport(player)){

View File

@ -10,6 +10,7 @@ import chylex.hee.game.world.provider.DragonFightManagerNull
import chylex.hee.game.world.provider.WorldBorderNull
import chylex.hee.game.world.territory.TerritoryInstance
import chylex.hee.game.world.territory.TerritoryInstance.Companion.THE_HUB_INSTANCE
import chylex.hee.game.world.territory.TerritoryTicker
import chylex.hee.game.world.territory.TerritoryVoid
import chylex.hee.proxy.ISidedProxy
import chylex.hee.system.forge.EventPriority
@ -77,6 +78,9 @@ class WorldProviderEndCustom(world: World, type: DimensionType) : EndDimension(w
private val clientEnvironment
get() = clientProxy?.getClientSidePlayer()?.let(TerritoryInstance.Companion::fromPos)?.let { it.territory.desc.environment }
private val serverWorld
get() = world as ServerWorld
init{
when(val w = this.world){
is ServerWorld -> {
@ -124,8 +128,10 @@ class WorldProviderEndCustom(world: World, type: DimensionType) : EndDimension(w
// Behavior properties
override fun tick(){ // stops triggering a few seconds after all players leave the dimension (if still loaded)
TerritoryTicker.onWorldTick(serverWorld)
if (!debugMode){
TerritoryVoid.onWorldTick(world as ServerWorld)
TerritoryVoid.onWorldTick(serverWorld)
}
}

View File

@ -2,6 +2,7 @@ package chylex.hee.game.world.territory
import chylex.hee.game.world.territory.properties.TerritoryColors
import chylex.hee.game.world.territory.properties.TerritoryEnvironment
import chylex.hee.game.world.territory.properties.TerritoryTokenHolders
import chylex.hee.game.world.territory.storage.TerritoryEntry
interface ITerritoryDescription{
val colors: TerritoryColors
@ -10,4 +11,8 @@ interface ITerritoryDescription{
@JvmDefault
val tokenHolders: TerritoryTokenHolders
get() = TerritoryTokenHolders.Default
@JvmDefault
fun initialize(instance: TerritoryInstance, entry: TerritoryEntry, tickers: MutableList<ITerritoryTicker>){
}
}

View File

@ -0,0 +1,13 @@
package chylex.hee.game.world.territory
import net.minecraft.world.World
interface ITerritoryTicker{
/**
* If this value matches the current world tick, all players inside the ticker's territory will be sent a new [chylex.hee.network.client.PacketClientTerritoryEnvironment].
*/
@JvmDefault
val resendClientEnvironmentPacketOnWorldTick: Long
get() = Long.MIN_VALUE
fun tick(world: World)
}

View File

@ -0,0 +1,82 @@
package chylex.hee.game.world.territory
import chylex.hee.HEE
import chylex.hee.game.world.territory.storage.TerritoryGlobalStorage
import chylex.hee.game.world.totalTime
import chylex.hee.network.client.PacketClientTerritoryEnvironment
import chylex.hee.system.forge.EventPriority
import chylex.hee.system.forge.SubscribeAllEvents
import chylex.hee.system.forge.SubscribeEvent
import net.minecraft.world.World
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerChangedDimensionEvent
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent
import net.minecraftforge.event.world.WorldEvent
import java.util.UUID
@SubscribeAllEvents(modid = HEE.ID)
object TerritoryTicker{
private val active = mutableMapOf<TerritoryInstance, Entry>()
private val lastTerritory = mutableMapOf<UUID, TerritoryInstance>()
private class Entry(val tickers: List<ITerritoryTicker>){
var lastTickTime = -1L
}
fun onWorldTick(world: World){
val currentTime = world.totalTime
if (currentTime % 600L == 0L){
active.values.removeAll { currentTime - it.lastTickTime > 1L }
}
for(player in world.players){
val instance = TerritoryInstance.fromPos(player)
if (instance == null){
lastTerritory.remove(player.uniqueID)
continue
}
val storage = TerritoryGlobalStorage.get().forInstance(instance)
if (storage == null){
continue
}
val entry = active.getOrPut(instance){
Entry(mutableListOf<ITerritoryTicker>().also {
instance.territory.desc.initialize(instance, storage, it)
})
}
if (entry.lastTickTime != currentTime){
entry.lastTickTime = currentTime
entry.tickers.forEach { it.tick(world) }
}
if (instance != lastTerritory.put(player.uniqueID, instance) || entry.tickers.any { it.resendClientEnvironmentPacketOnWorldTick == currentTime }){
PacketClientTerritoryEnvironment(storage).sendToPlayer(player)
}
}
}
@SubscribeEvent
fun onWorldSave(e: WorldEvent.Save){
if (e.world.dimension.type !== HEE.dim){
return
}
active.clear()
}
@SubscribeEvent(priority = EventPriority.LOWEST)
fun onPlayerChangedDimension(e: PlayerChangedDimensionEvent){
if (e.to !== HEE.dim){
lastTerritory.remove(e.player.uniqueID)
}
}
@SubscribeEvent(priority = EventPriority.LOWEST)
fun onPlayerLoggedOut(e: PlayerLoggedOutEvent){
lastTerritory.remove(e.player.uniqueID)
}
}

View File

@ -1,4 +1,6 @@
package chylex.hee.game.world.territory.storage
import chylex.hee.HEE
import chylex.hee.game.item.ItemPortalToken.TokenType
import chylex.hee.game.world.ChunkGeneratorEndCustom
import chylex.hee.game.world.Pos
import chylex.hee.game.world.center
@ -7,28 +9,40 @@ import chylex.hee.system.delegate.NotifyOnChange
import chylex.hee.system.math.toYaw
import chylex.hee.system.migration.EntityPlayer
import chylex.hee.system.serialization.TagCompound
import chylex.hee.system.serialization.getCompoundOrNull
import chylex.hee.system.serialization.getEnum
import chylex.hee.system.serialization.getLongArrayOrNull
import chylex.hee.system.serialization.getPosOrNull
import chylex.hee.system.serialization.putEnum
import chylex.hee.system.serialization.putPos
import chylex.hee.system.serialization.use
import net.minecraft.util.math.BlockPos
import net.minecraftforge.common.util.INBTSerializable
import java.util.UUID
class TerritoryEntry(private val owner: TerritoryGlobalStorage, private val instance: TerritoryInstance) : INBTSerializable<TagCompound>{
private companion object{
class TerritoryEntry(private val owner: TerritoryGlobalStorage, private val instance: TerritoryInstance, val type: TokenType) : INBTSerializable<TagCompound>{
companion object{
private const val TYPE_TAG = "Type"
private const val SPAWN_POINT_TAG = "Spawn"
private const val INTEREST_POINT_TAG = "Interest"
private const val LAST_PORTALS_TAG = "LastPortals"
private const val COMPONENTS_TAG = "Components"
fun fromTag(owner: TerritoryGlobalStorage, instance: TerritoryInstance, tag: TagCompound): TerritoryEntry{
return TerritoryEntry(owner, instance, tag.getEnum<TokenType>(TYPE_TAG) ?: TokenType.NORMAL).also { it.deserializeNBT(tag) }
}
}
private var spawnPoint: BlockPos? by NotifyOnChange(null, owner::markDirty)
private var interestPoint: BlockPos? by NotifyOnChange(null, owner::markDirty)
val markDirty = owner::markDirty
private var spawnPoint: BlockPos? by NotifyOnChange(null, markDirty)
private var interestPoint: BlockPos? by NotifyOnChange(null, markDirty)
val spawnYaw
get() = interestPoint?.center?.let { ip -> spawnPoint?.center?.let { sp -> ip.subtract(sp).toYaw() } }
private val lastPortals = mutableMapOf<UUID, BlockPos>()
private val components = mutableMapOf<Class<out TerritoryStorageComponent>, TerritoryStorageComponent>()
fun loadSpawn(): BlockPos{
if (spawnPoint == null){
@ -59,7 +73,25 @@ class TerritoryEntry(private val owner: TerritoryGlobalStorage, private val inst
}
}
fun <T : TerritoryStorageComponent> registerComponent(info: Pair<Class<T>, String>): T{
@Suppress("UNCHECKED_CAST")
return components.getOrPut(info.first){ TerritoryStorageComponent.getComponentConstructor(info.second)!!(markDirty) } as T
}
fun <T : TerritoryStorageComponent> getComponent(cls: Class<T>): T?{
@Suppress("UNCHECKED_CAST")
return components[cls]?.let { it as T }
}
inline fun <reified T : TerritoryStorageComponent> getComponent(): T?{
return getComponent(T::class.java)
}
override fun serializeNBT() = TagCompound().apply {
if (this@TerritoryEntry.type != TokenType.NORMAL){
putEnum(TYPE_TAG, this@TerritoryEntry.type)
}
spawnPoint?.let {
putPos(SPAWN_POINT_TAG, it)
}
@ -82,6 +114,23 @@ class TerritoryEntry(private val owner: TerritoryGlobalStorage, private val inst
putLongArray(LAST_PORTALS_TAG, lastPortalArray)
}
if (components.isNotEmpty()){
val componentTag = TagCompound()
for(component in components.values){
val name = TerritoryStorageComponent.getComponentName(component)
if (name == null){
HEE.log.error("[TerritoryEntry] could not map storage component ${component.javaClass.name} to its name")
}
else{
componentTag.put(name, component.serializeNBT())
}
}
put(COMPONENTS_TAG, componentTag)
}
}
override fun deserializeNBT(nbt: TagCompound) = nbt.use {
@ -97,5 +146,23 @@ class TerritoryEntry(private val owner: TerritoryGlobalStorage, private val inst
lastPortals[UUID(lastPortalArray[index + 0], lastPortalArray[index + 1])] = Pos(lastPortalArray[2])
}
}
components.clear()
val componentTag = getCompoundOrNull(COMPONENTS_TAG)
if (componentTag != null){
for(name in componentTag.keySet()){
val constructor = TerritoryStorageComponent.getComponentConstructor(name)
if (constructor == null){
HEE.log.error("[TerritoryEntry] could not map storage component name $name to its constructor")
}
else{
val component = constructor(markDirty).also { it.deserializeNBT(componentTag.getCompound(name)) }
components[component.javaClass] = component
}
}
}
}
}

View File

@ -1,4 +1,5 @@
package chylex.hee.game.world.territory.storage
import chylex.hee.game.item.ItemPortalToken.TokenType
import chylex.hee.game.world.perDimensionData
import chylex.hee.game.world.territory.TerritoryInstance
import chylex.hee.game.world.territory.TerritoryInstance.Companion.THE_HUB_INSTANCE
@ -20,14 +21,18 @@ class TerritoryGlobalStorage private constructor() : WorldSavedData(NAME){
// Instance
private val spawnEntry = TerritoryEntry(this, THE_HUB_INSTANCE)
private val spawnEntry = TerritoryEntry(this, THE_HUB_INSTANCE, TokenType.NORMAL)
private val territoryData = EnumMap(TerritoryType.values().filterNot { it.isSpawn }.associateWith { mutableListOf<TerritoryEntry>() })
private fun makeEntry(territory: TerritoryType, index: Int): TerritoryEntry{
return TerritoryEntry(this, TerritoryInstance(territory, index))
private fun makeEntry(territory: TerritoryType, index: Int, type: TokenType): TerritoryEntry{
return TerritoryEntry(this, TerritoryInstance(territory, index), type)
}
fun assignNewIndex(territory: TerritoryType): Int{
private fun makeEntry(territory: TerritoryType, index: Int, tag: TagCompound): TerritoryEntry{
return TerritoryEntry.fromTag(this, TerritoryInstance(territory, index), tag)
}
fun assignNewIndex(territory: TerritoryType, tokenType: TokenType): Int{
if (territory.isSpawn){
return 0
}
@ -35,7 +40,7 @@ class TerritoryGlobalStorage private constructor() : WorldSavedData(NAME){
val list = territoryData.getValue(territory)
val newIndex = list.size
list.add(makeEntry(territory, newIndex))
list.add(makeEntry(territory, newIndex, tokenType))
markDirty()
return newIndex
@ -68,7 +73,7 @@ class TerritoryGlobalStorage private constructor() : WorldSavedData(NAME){
val list = territoryData.getValue(territory)
list.clear()
list.addAll(getListOfCompounds(key).mapIndexed { index, nbt -> makeEntry(territory, index).also { it.deserializeNBT(nbt) } })
list.addAll(getListOfCompounds(key).mapIndexed { index, nbt -> makeEntry(territory, index, nbt) })
}
isDirty = false

View File

@ -0,0 +1,21 @@
package chylex.hee.game.world.territory.storage
import chylex.hee.system.serialization.TagCompound
import net.minecraftforge.common.util.INBTSerializable
abstract class TerritoryStorageComponent : INBTSerializable<TagCompound>{
companion object{
private val CLASS_TO_STRING = mapOf<Class<out TerritoryStorageComponent>, String>(
)
private val STRING_TO_INSTANCE = mapOf<String, (() -> Unit) -> TerritoryStorageComponent>(
)
fun getComponentName(component: TerritoryStorageComponent): String?{
return CLASS_TO_STRING[component.javaClass]
}
fun getComponentConstructor(name: String): ((() -> Unit) -> TerritoryStorageComponent)?{
return STRING_TO_INSTANCE[name]
}
}
}

View File

@ -6,6 +6,7 @@ import chylex.hee.network.client.PacketClientMoveYourAss
import chylex.hee.network.client.PacketClientPotionDuration
import chylex.hee.network.client.PacketClientRotateInstantly
import chylex.hee.network.client.PacketClientTeleportInstantly
import chylex.hee.network.client.PacketClientTerritoryEnvironment
import chylex.hee.network.client.PacketClientTrinketBreak
import chylex.hee.network.client.PacketClientUpdateExperience
import chylex.hee.network.client.PacketClientWeather
@ -24,6 +25,7 @@ object ModPackets{
build<PacketClientPotionDuration>(),
build<PacketClientRotateInstantly>(),
build<PacketClientTeleportInstantly>(),
build<PacketClientTerritoryEnvironment>(),
build<PacketClientTrinketBreak>(),
build<PacketClientUpdateExperience>(),
build<PacketClientWeather>(),

View File

@ -0,0 +1,40 @@
package chylex.hee.network.client
import chylex.hee.game.world.territory.storage.TerritoryEntry
import chylex.hee.network.BaseClientPacket
import chylex.hee.system.forge.Side
import chylex.hee.system.forge.Sided
import chylex.hee.system.migration.EntityPlayerSP
import chylex.hee.system.serialization.TagCompound
import chylex.hee.system.serialization.readTag
import chylex.hee.system.serialization.use
import chylex.hee.system.serialization.writeTag
import net.minecraft.network.PacketBuffer
class PacketClientTerritoryEnvironment() : BaseClientPacket(){
constructor(entry: TerritoryEntry) : this(){
}
override fun write(buffer: PacketBuffer) = buffer.use {
}
override fun read(buffer: PacketBuffer) = buffer.use {
}
@Sided(Side.CLIENT)
override fun handle(player: EntityPlayerSP){
}
private fun PacketBuffer.writeOptionalTag(tag: TagCompound?){
if (tag == null){
writeBoolean(false)
}
else{
writeBoolean(true)
writeTag(tag)
}
}
private fun PacketBuffer.readOptionalTag(): TagCompound?{
return if (readBoolean()) readTag() else null
}
}