feat: swarm projected entity
This commit is contained in:
parent
b3743bf624
commit
23b30a1084
7
Makefile
Normal file
7
Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
common/src/main/resources/data/quaedam/projected-person-names:
|
||||
curl -Ls https://github.com/wainshine/Chinese-Names-Corpus/raw/master/English_Names_Corpus/English_Names_Corpus%EF%BC%882W%EF%BC%89.txt | tail -n +4 | shuf | head -n 2500 > $@.tmp
|
||||
curl -Ls https://github.com/wainshine/Chinese-Names-Corpus/raw/master/Chinese_Names_Corpus/Chinese_Names_Corpus%EF%BC%88120W%EF%BC%89.txt | tail -n +4 | shuf | head -n 2500 >> $@.tmp
|
||||
cat $@.tmp | tr -d '\015' > $@
|
||||
rm $@.tmp
|
||||
|
||||
.PHONY: common/src/main/resources/data/quaedam/projected-person-names
|
@ -8,6 +8,7 @@ import net.minecraft.network.chat.Component
|
||||
import net.minecraft.world.item.CreativeModeTab
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import org.slf4j.LoggerFactory
|
||||
import quaedam.projection.ProjectionEffectType
|
||||
import quaedam.projection.SkylightProjection
|
||||
import quaedam.projection.swarm.SwarmProjection
|
||||
@ -17,10 +18,13 @@ object Quaedam {
|
||||
|
||||
const val ID = "quaedam"
|
||||
|
||||
val logger = LoggerFactory.getLogger("Quaedam")
|
||||
|
||||
val creativeModeTabs = DeferredRegister.create(ID, Registries.CREATIVE_MODE_TAB)!!
|
||||
val items = DeferredRegister.create(ID, Registries.ITEM)!!
|
||||
val blocks = DeferredRegister.create(ID, Registries.BLOCK)!!
|
||||
val blockEntities = DeferredRegister.create(ID, Registries.BLOCK_ENTITY_TYPE)!!
|
||||
val entities = DeferredRegister.create(ID, Registries.ENTITY_TYPE)!!
|
||||
val projectionEffects = DeferredRegister.create(ID, ProjectionEffectType.registryKey)!!
|
||||
|
||||
val creativeModeTab: RegistrySupplier<CreativeModeTab> = creativeModeTabs.register("quaedam") {
|
||||
@ -38,6 +42,7 @@ object Quaedam {
|
||||
items.register()
|
||||
blocks.register()
|
||||
blockEntities.register()
|
||||
entities.register()
|
||||
projectionEffects.register()
|
||||
}
|
||||
|
||||
|
@ -28,8 +28,6 @@ abstract class ProjectionEffect {
|
||||
|
||||
open fun deactivate(level: Level, pos: BlockPos) {}
|
||||
|
||||
open fun update(level: Level, pos: BlockPos, old: ProjectionEffect) {}
|
||||
|
||||
open fun randomTick(level: ServerLevel, pos: BlockPos) {}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,82 @@
|
||||
package quaedam.projection.swarm
|
||||
|
||||
import dev.architectury.registry.client.level.entity.EntityRendererRegistry
|
||||
import dev.architectury.registry.level.entity.EntityAttributeRegistry
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.syncher.EntityDataAccessor
|
||||
import net.minecraft.network.syncher.EntityDataSerializers
|
||||
import net.minecraft.network.syncher.SynchedEntityData
|
||||
import net.minecraft.world.DifficultyInstance
|
||||
import net.minecraft.world.entity.*
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.ServerLevelAccessor
|
||||
import quaedam.Quaedam
|
||||
|
||||
class ProjectedPersonEntity(entityType: EntityType<out PathfinderMob>, level: Level) :
|
||||
PathfinderMob(entityType, level) {
|
||||
|
||||
companion object {
|
||||
|
||||
const val ID = "projected_person"
|
||||
|
||||
val entity = Quaedam.entities.register(ID) {
|
||||
EntityType.Builder.of(::ProjectedPersonEntity, MobCategory.CREATURE)
|
||||
.canSpawnFarFromPlayer()
|
||||
.sized(2.0f, 2.0f)
|
||||
.build("quaedam:$ID")
|
||||
}!!
|
||||
|
||||
val dataShape =
|
||||
SynchedEntityData.defineId(ProjectedPersonEntity::class.java, EntityDataSerializers.COMPOUND_TAG)
|
||||
|
||||
private fun createAttributes(): AttributeSupplier.Builder = Mob.createMobAttributes()
|
||||
.add(Attributes.ATTACK_DAMAGE, 1.5)
|
||||
.add(Attributes.MOVEMENT_SPEED, 0.11)
|
||||
.add(Attributes.ATTACK_SPEED)
|
||||
|
||||
init {
|
||||
EntityAttributeRegistry.register(entity, ::createAttributes)
|
||||
EntityRendererRegistry.register(entity, ::ProjectedPersonRenderer)
|
||||
ProjectedPersonShape
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun finalizeSpawn(
|
||||
serverLevelAccessor: ServerLevelAccessor,
|
||||
difficultyInstance: DifficultyInstance,
|
||||
mobSpawnType: MobSpawnType,
|
||||
spawnGroupData: SpawnGroupData?,
|
||||
compoundTag: CompoundTag?
|
||||
): SpawnGroupData? {
|
||||
shape = ProjectedPersonShape.create(serverLevelAccessor.random.nextLong())
|
||||
return super.finalizeSpawn(serverLevelAccessor, difficultyInstance, mobSpawnType, spawnGroupData, compoundTag)
|
||||
}
|
||||
|
||||
override fun defineSynchedData() {
|
||||
super.defineSynchedData()
|
||||
entityData.define(dataShape, CompoundTag())
|
||||
}
|
||||
|
||||
private var shapeTag
|
||||
get() = entityData.get(dataShape)
|
||||
set(value) = entityData.set(dataShape, value)
|
||||
|
||||
var shape = ProjectedPersonShape()
|
||||
set(value) {
|
||||
field = value
|
||||
shapeTag = shape.toTag()
|
||||
}
|
||||
|
||||
override fun onSyncedDataUpdated(data: EntityDataAccessor<*>) {
|
||||
if (data == dataShape) {
|
||||
shape = ProjectedPersonShape.fromTag(shapeTag)
|
||||
}
|
||||
super.onSyncedDataUpdated(data)
|
||||
}
|
||||
|
||||
override fun shouldShowName() = true
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package quaedam.projection.swarm
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.client.model.PlayerModel
|
||||
import net.minecraft.client.model.geom.ModelLayers
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider
|
||||
import net.minecraft.client.renderer.entity.MobRenderer
|
||||
import net.minecraft.client.renderer.entity.layers.CustomHeadLayer
|
||||
import net.minecraft.client.renderer.entity.layers.ItemInHandLayer
|
||||
|
||||
class ProjectedPersonRenderer(context: EntityRendererProvider.Context) :
|
||||
MobRenderer<ProjectedPersonEntity, PlayerModel<ProjectedPersonEntity>>(
|
||||
context,
|
||||
PlayerModel(context.bakeLayer(ModelLayers.PLAYER), false),
|
||||
0.4f
|
||||
) {
|
||||
|
||||
init {
|
||||
addLayer(CustomHeadLayer(this, context.modelSet, context.itemInHandRenderer))
|
||||
addLayer(ItemInHandLayer(this, context.itemInHandRenderer))
|
||||
}
|
||||
|
||||
override fun getTextureLocation(entity: ProjectedPersonEntity) = ProjectedPersonShape.Skins[entity.shape.skin]
|
||||
|
||||
override fun scale(entity: ProjectedPersonEntity, poseStack: PoseStack, f: Float) {
|
||||
poseStack.scale(entity.shape.scaleX, entity.shape.scaleY, entity.shape.scaleZ)
|
||||
super.scale(entity, poseStack, f)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
package quaedam.projection.swarm
|
||||
|
||||
import dev.architectury.registry.ReloadListenerRegistry
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.server.packs.resources.PreparableReloadListener
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
import net.minecraft.util.profiling.ProfilerFiller
|
||||
import quaedam.Quaedam
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.Executor
|
||||
import java.util.function.IntFunction
|
||||
import java.util.stream.Collectors
|
||||
import kotlin.math.abs
|
||||
import kotlin.random.Random
|
||||
import kotlin.random.nextInt
|
||||
|
||||
data class ProjectedPersonShape(
|
||||
val scaleX: Float = 1.0f,
|
||||
val scaleY: Float = 1.0f,
|
||||
val scaleZ: Float = 1.0f,
|
||||
val name: String = "[DESYNC]",
|
||||
val skin: Int = 0,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
||||
const val KEY_SCALE_X = "ScaleX"
|
||||
const val KEY_SCALE_Y = "ScaleY"
|
||||
const val KEY_SCALE_Z = "ScaleZ"
|
||||
const val KEY_NAME = "Name"
|
||||
const val KEY_SKIN = "Skin"
|
||||
|
||||
init {
|
||||
Names
|
||||
Skins
|
||||
}
|
||||
|
||||
fun create(seed: Long) = create(Random(seed))
|
||||
|
||||
fun create(rand: Random) = ProjectedPersonShape(
|
||||
scaleX = rand.nextInt(0..6) * 0.1f + 0.7f,
|
||||
scaleY = rand.nextInt(0..6) * 0.1f + 0.7f,
|
||||
scaleZ = rand.nextInt(0..2) * 0.1f + 0.9f,
|
||||
name = Names.random(rand),
|
||||
skin = Skins.random(rand),
|
||||
)
|
||||
|
||||
fun fromTag(tag: CompoundTag) = ProjectedPersonShape(
|
||||
scaleX = tag.getFloat(KEY_SCALE_X),
|
||||
scaleY = tag.getFloat(KEY_SCALE_Y),
|
||||
scaleZ = tag.getFloat(KEY_SCALE_Z),
|
||||
name = tag.getString(KEY_NAME),
|
||||
skin = tag.getInt(KEY_SKIN),
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
fun toTag() = CompoundTag().apply {
|
||||
putFloat(KEY_SCALE_X, scaleX)
|
||||
putFloat(KEY_SCALE_Y, scaleY)
|
||||
putFloat(KEY_SCALE_Z, scaleZ)
|
||||
putString(KEY_NAME, name)
|
||||
putInt(KEY_SKIN, skin)
|
||||
}
|
||||
|
||||
object Names {
|
||||
|
||||
val id = ResourceLocation("quaedam", "projected-person-names")
|
||||
|
||||
var names = emptySet<String>()
|
||||
|
||||
init {
|
||||
ReloadListenerRegistry.register(PackType.SERVER_DATA, ReloadListener, id)
|
||||
}
|
||||
|
||||
fun random(random: Random) = names.random(random)
|
||||
|
||||
private object ReloadListener : PreparableReloadListener {
|
||||
|
||||
@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
|
||||
override fun reload(
|
||||
preparationBarrier: PreparableReloadListener.PreparationBarrier,
|
||||
resourceManager: ResourceManager,
|
||||
profilerFiller: ProfilerFiller,
|
||||
profilerFiller2: ProfilerFiller,
|
||||
executor: Executor,
|
||||
executor2: Executor
|
||||
): CompletableFuture<Void> = preparationBarrier.wait(null)
|
||||
.thenAcceptAsync({
|
||||
names = resourceManager.getResource(id).get().openAsReader().use { it.readLines() }
|
||||
.filterNot { it.isBlank() }
|
||||
.filterNot { it.startsWith("#") }
|
||||
.toSet()
|
||||
Quaedam.logger.info("Loaded ${names.size} unique projected person names")
|
||||
}, executor2)
|
||||
|
||||
override fun getName() = "quaedam:projected_person_names"
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object Skins {
|
||||
|
||||
val id = ResourceLocation("quaedam", "skins")
|
||||
|
||||
// only available on client
|
||||
var skins = emptyList<ResourceLocation>()
|
||||
|
||||
init {
|
||||
ReloadListenerRegistry.register(PackType.CLIENT_RESOURCES, ReloadListener, id)
|
||||
}
|
||||
|
||||
operator fun get(index: Int) = skins[abs(index) % skins.size]
|
||||
fun random(random: Random) = random.nextInt()
|
||||
|
||||
private object ReloadListener : PreparableReloadListener {
|
||||
|
||||
@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
|
||||
override fun reload(
|
||||
preparationBarrier: PreparableReloadListener.PreparationBarrier,
|
||||
resourceManager: ResourceManager,
|
||||
profilerFiller: ProfilerFiller,
|
||||
profilerFiller2: ProfilerFiller,
|
||||
executor: Executor,
|
||||
executor2: Executor
|
||||
): CompletableFuture<Void> = preparationBarrier.wait(null)
|
||||
.thenAcceptAsync({
|
||||
val skins = mutableSetOf<ResourceLocation>()
|
||||
skins.addAll(resourceManager.listResources("textures/entity/player/wide") { it.path.endsWith(".png") }.keys)
|
||||
skins.addAll(resourceManager.listResources("textures/entity/projected_person") { it.namespace == "quaedam" }.keys)
|
||||
Skins.skins = skins.toSet().toList().sorted()
|
||||
Quaedam.logger.info("Loaded ${Skins.skins.size} unique projected person skins")
|
||||
Quaedam.logger.debug("Projected person skins ring: $skins")
|
||||
}, executor2)
|
||||
|
||||
override fun getName() = "quaedam:projected_person_skins"
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -23,4 +23,8 @@ object SwarmProjection {
|
||||
ProjectionEffectType { SwarmProjectionEffect() }
|
||||
}!!
|
||||
|
||||
init {
|
||||
ProjectedPersonEntity
|
||||
}
|
||||
|
||||
}
|
@ -12,6 +12,7 @@ import net.minecraft.world.level.ChunkPos
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.levelgen.structure.BoundingBox
|
||||
import quaedam.Quaedam
|
||||
import quaedam.projection.ProjectionEffect
|
||||
import quaedam.projection.ProjectionEffectType
|
||||
import quaedam.projection.ProjectionProvider
|
||||
@ -62,7 +63,7 @@ class ProjectorBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
}
|
||||
}
|
||||
}
|
||||
updateEffects(effects)
|
||||
updateEffects(effects, notify = false)
|
||||
}
|
||||
|
||||
override fun getUpdateTag(): CompoundTag = saveWithoutMetadata()
|
||||
@ -89,17 +90,22 @@ class ProjectorBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
fun updateEffects(effects: Map<ProjectionEffectType<*>, ProjectionEffect>, notify: Boolean = true) {
|
||||
if (effects != this.effects) {
|
||||
val oldEffects = this.effects
|
||||
val level = level!!
|
||||
this.effects = effects
|
||||
if (!level.isClientSide) {
|
||||
if (level != null) {
|
||||
val level = level!!
|
||||
if (level.isClientSide && notify) {
|
||||
sendBlockUpdated()
|
||||
}
|
||||
val addedEffects = effects.filterKeys { it !in oldEffects }
|
||||
val removedEffects = oldEffects.filterKeys { it !in effects }
|
||||
val updatedEffects = effects.filter { (k, v) -> oldEffects[k] != v }
|
||||
val updatedEffects = effects.filter { (k, v) -> oldEffects[k] != null && oldEffects[k] != v }
|
||||
addedEffects.values.forEach { it.activate(level, blockPos) }
|
||||
removedEffects.values.forEach { it.deactivate(level, blockPos) }
|
||||
updatedEffects.forEach { (k, v) -> v.update(level, blockPos, oldEffects[k]!!) }
|
||||
updatedEffects.forEach { (k, v) ->
|
||||
oldEffects[k]!!.deactivate(level, blockPos)
|
||||
v.activate(level, blockPos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
5000
common/src/main/resources/data/quaedam/projected-person-names
Normal file
5000
common/src/main/resources/data/quaedam/projected-person-names
Normal file
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,7 @@ architectury {
|
||||
loom {
|
||||
accessWidenerPath.set(project(":common").loom.accessWidenerPath)
|
||||
|
||||
forge.apply {
|
||||
forge {
|
||||
convertAccessWideners.set(true)
|
||||
extraAccessWideners.add(loom.accessWidenerPath.get().asFile.name)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user