Skip to content

Commit e98cff1

Browse files
feat: entity climate variants (#444)
1 parent 7a2b152 commit e98cff1

20 files changed

+306
-26
lines changed

src/main/java/cn/nukkit/Server.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,10 @@ public Level remove(@NotNull Object key) {
548548
* Enable 1.21 paintings
549549
*/
550550
public boolean enableNewPaintings;
551+
/**
552+
* Enable chicken egg laying from 1.21.70
553+
*/
554+
public boolean enableNewChickenEggsLaying;
551555
/**
552556
* A number of datagram packets each address can send within one RakNet tick (10ms)
553557
*/
@@ -825,6 +829,7 @@ public Level remove(@NotNull Object key) {
825829
this.enablePlugins(PluginLoadOrder.POSTWORLD);
826830
}
827831

832+
EntityProperty.init();
828833
EntityProperty.buildPacket();
829834
EntityProperty.buildPlayerProperty();
830835

@@ -3142,6 +3147,7 @@ private void loadSettings() {
31423147
this.useNativeLevelDB = this.getPropertyBoolean("use-native-leveldb", false);
31433148
this.enableRawOres = this.getPropertyBoolean("enable-raw-ores", true);
31443149
this.enableNewPaintings = this.getPropertyBoolean("enable-new-paintings", true);
3150+
this.enableNewChickenEggsLaying = this.getPropertyBoolean("enable-new-chicken-eggs-laying", true);
31453151
this.rakPacketLimit = this.getPropertyInt("rak-packet-limit", RakConstants.DEFAULT_PACKET_LIMIT);
31463152
this.enableRakSendCookie = this.getPropertyBoolean("enable-rak-send-cookie", true);
31473153
}
@@ -3294,6 +3300,7 @@ private static class ServerProperties extends ConfigSection {
32943300
put("use-native-leveldb", false);
32953301
put("enable-raw-ores", true);
32963302
put("enable-new-paintings", true);
3303+
put("enable-new-chicken-eggs-laying", true);
32973304
}
32983305
}
32993306

src/main/java/cn/nukkit/entity/Entity.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1434,6 +1434,8 @@ protected DataPacket createAddEntityPacket() {
14341434
addEntity.links[i] = new EntityLink(this.id, this.passengers.get(i).id, i == 0 ? EntityLink.TYPE_RIDER : TYPE_PASSENGER, false, false, 0f);
14351435
}
14361436

1437+
addEntity.properties = this.propertySyncData();
1438+
14371439
return addEntity;
14381440
}
14391441

@@ -3335,7 +3337,7 @@ public final boolean setEnumEntityProperty(String identifier, String value) {
33353337
List<EntityProperty> entityPropertyList = EntityProperty.getEntityProperty(this.getIdentifier().toString());
33363338

33373339
for (EntityProperty property : entityPropertyList) {
3338-
if(property.getIdentifier() == identifier && property instanceof EnumEntityProperty enumEntityProperty) {
3340+
if(Objects.equals(property.getIdentifier(), identifier) && property instanceof EnumEntityProperty enumEntityProperty) {
33393341
int index = enumEntityProperty.findIndex(value);
33403342

33413343
if(index >= 0) {
@@ -3348,6 +3350,19 @@ public final boolean setEnumEntityProperty(String identifier, String value) {
33483350
return false;
33493351
}
33503352

3353+
public final String getEnumEntityProperty(String identifier) {
3354+
List<EntityProperty> entityPropertyList = EntityProperty.getEntityProperty(this.getIdentifier().toString());
3355+
3356+
for (EntityProperty property : entityPropertyList) {
3357+
if (!identifier.equals(property.getIdentifier()) ||
3358+
!(property instanceof EnumEntityProperty enumProperty)) {
3359+
continue;
3360+
}
3361+
return enumProperty.getEnums()[intProperties.get(identifier)];
3362+
}
3363+
return null;
3364+
}
3365+
33513366
private void initEntityProperties() {
33523367
if(this.getIdentifier() != null) {
33533368
initEntityProperties(this.getIdentifier().toString());
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package cn.nukkit.entity;
2+
3+
import cn.nukkit.Player;
4+
import cn.nukkit.level.biome.Biome;
5+
import cn.nukkit.nbt.tag.CompoundTag;
6+
import cn.nukkit.nbt.tag.ListTag;
7+
import cn.nukkit.nbt.tag.StringTag;
8+
import lombok.Getter;
9+
import org.jetbrains.annotations.Nullable;
10+
11+
import java.util.Arrays;
12+
13+
/**
14+
* Ported from PowerNukkitX
15+
*/
16+
public interface EntityClimateVariant {
17+
18+
String PROPERTY_STATE = "minecraft:climate_variant";
19+
20+
StringTag TAG_SPAWNS_WARM_VARIANT_FARM_ANIMALS = new StringTag("", "spawns_warm_variant_farm_animals");
21+
StringTag TAG_SPAWNS_COLD_VARIANT_FARM_ANIMALS = new StringTag("", "spawns_cold_variant_farm_animals");
22+
23+
default Variant getBiomeVariant(int biomeId) {
24+
CompoundTag biomeDefinitions = Biome.getBiomeDefinitions(biomeId);
25+
if (biomeDefinitions != null) {
26+
ListTag<StringTag> tags = biomeDefinitions.getList("tags", StringTag.class);
27+
if (tags.contains(TAG_SPAWNS_WARM_VARIANT_FARM_ANIMALS)) {
28+
return Variant.WARM;
29+
} else if (tags.contains(TAG_SPAWNS_COLD_VARIANT_FARM_ANIMALS)) {
30+
return Variant.COLD;
31+
}
32+
}
33+
34+
return Variant.TEMPERATE;
35+
}
36+
37+
@Nullable
38+
default Variant getVariant() {
39+
if (this instanceof Entity entity) {
40+
String var = entity.getEnumEntityProperty(PROPERTY_STATE);
41+
if (var == null) return null;
42+
return Arrays.stream(Variant.VALUES).filter(variant -> variant.getName().equals(var)).findFirst().get();
43+
}
44+
return null;
45+
}
46+
47+
default void setVariant(Variant variant) {
48+
if (this instanceof Entity entity) {
49+
entity.setEnumEntityProperty(PROPERTY_STATE, variant.getName());
50+
entity.sendData(entity.getViewers().values().toArray(Player[]::new));
51+
entity.namedTag.putString("variant", variant.getName());
52+
}
53+
}
54+
55+
enum Variant {
56+
57+
TEMPERATE("temperate"),
58+
WARM("warm"),
59+
COLD("cold");
60+
61+
@Getter
62+
private final String name;
63+
64+
Variant(String s) {
65+
name = s;
66+
}
67+
68+
public static Variant get(String name) {
69+
return Arrays.stream(Variant.VALUES).filter(variant -> variant.getName().equals(name)).findFirst().get();
70+
}
71+
72+
public final static Variant[] VALUES = values();
73+
}
74+
75+
}

src/main/java/cn/nukkit/entity/data/property/EntityProperty.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package cn.nukkit.entity.data.property;
22

3+
import cn.nukkit.nbt.NBTIO;
34
import cn.nukkit.nbt.tag.CompoundTag;
45
import cn.nukkit.nbt.tag.ListTag;
6+
import cn.nukkit.nbt.tag.StringTag;
57
import cn.nukkit.network.protocol.SyncEntityPropertyPacket;
68

9+
import java.io.IOException;
710
import java.util.*;
811

912
/**
@@ -24,6 +27,37 @@ public EntityProperty(String identifier) {
2427
this.identifier = identifier;
2528
}
2629

30+
public static void init() {
31+
try (var stream = EntityProperty.class.getClassLoader().getResourceAsStream("entity_properties.nbt")) {
32+
CompoundTag root = NBTIO.readCompressed(stream);
33+
root.getTags().values().forEach(uncast -> {
34+
if(uncast instanceof CompoundTag tag) {
35+
ListTag<CompoundTag> properties = tag.getList("properties", CompoundTag.class);
36+
for(CompoundTag property : properties.getAll()) {
37+
String name = property.getString("name");
38+
int type = property.getInt("type");
39+
EntityProperty data = switch (type) {
40+
case 0 -> new IntEntityProperty(name, property.getInt("min"), property.getInt("max"), property.getInt("min"));
41+
case 1 -> new FloatEntityProperty(name, property.getInt("min"), property.getInt("max"), property.getInt("min"));
42+
case 2 -> new BooleanEntityProperty(name, false);
43+
case 3 -> {
44+
List<String> enums = new ArrayList<>();
45+
for(StringTag entry : property.getList("enum", StringTag.class).getAll()) {
46+
enums.add(entry.data);
47+
}
48+
yield new EnumEntityProperty(name, enums.toArray(String[]::new), enums.get(0));
49+
}
50+
default -> throw new IllegalArgumentException("Unknown EntityProperty type " + type);
51+
};
52+
register(tag.getString("type"), data);
53+
}
54+
}
55+
});
56+
} catch (IOException e) {
57+
throw new RuntimeException(e);
58+
}
59+
}
60+
2761
public static boolean register(String entityIdentifier, EntityProperty property) {
2862
List<EntityProperty> entityProperties = entityPropertyMap.getOrDefault(entityIdentifier, new ArrayList<>());
2963
for (EntityProperty entityProperty : entityProperties) {

src/main/java/cn/nukkit/entity/data/property/EnumEntityProperty.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public void populateTag(CompoundTag tag) {
4343
tag.putInt("type", 3);
4444
ListTag<StringTag> enumList = new ListTag<>();
4545
for (String enumValue : getEnums()) {
46-
enumList.add(new StringTag(enumValue));
46+
enumList.add(new StringTag("", enumValue));
4747
}
4848
tag.putList("enum", enumList);
4949
}

src/main/java/cn/nukkit/entity/passive/EntityChicken.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package cn.nukkit.entity.passive;
22

33
import cn.nukkit.Player;
4+
import cn.nukkit.Server;
5+
import cn.nukkit.entity.EntityClimateVariant;
46
import cn.nukkit.event.entity.EntityDamageEvent;
57
import cn.nukkit.item.Item;
68
import cn.nukkit.level.format.FullChunk;
@@ -13,7 +15,7 @@
1315
import java.util.ArrayList;
1416
import java.util.List;
1517

16-
public class EntityChicken extends EntityWalkingAnimal {
18+
public class EntityChicken extends EntityWalkingAnimal implements EntityClimateVariant {
1719

1820
public static final int NETWORK_ID = 10;
1921

@@ -59,9 +61,14 @@ public float getGravity() {
5961
@Override
6062
public void initEntity() {
6163
this.setMaxHealth(4);
62-
6364
super.initEntity();
6465

66+
if (namedTag.contains("variant")) {
67+
setVariant(Variant.get(namedTag.getString("variant")));
68+
} else {
69+
setVariant(getBiomeVariant(getLevel().getBiomeId(getFloorX(), getFloorZ())));
70+
}
71+
6572
if (this.namedTag.contains("EggLayTime")) {
6673
this.eggLayTime = this.namedTag.getInt("EggLayTime");
6774
} else {
@@ -84,7 +91,7 @@ public boolean entityBaseTick(int tickDiff) {
8491
if (this.eggLayTime > 0) {
8592
eggLayTime -= tickDiff;
8693
} else {
87-
this.level.dropItem(this, Item.get(Item.EGG, 0, 1));
94+
this.level.dropItem(this, getEgg());
8895
this.level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_PLOP);
8996
this.eggLayTime = getRandomEggLayTime();
9097
}
@@ -93,6 +100,15 @@ public boolean entityBaseTick(int tickDiff) {
93100
return hasUpdate;
94101
}
95102

103+
private Item getEgg() {
104+
if (Server.getInstance().enableNewChickenEggsLaying) {
105+
if(getVariant() == EntityClimateVariant.Variant.COLD) return Item.fromString(Item.BLUE_EGG);
106+
if(getVariant() == EntityClimateVariant.Variant.WARM) return Item.fromString(Item.BROWN_EGG);
107+
}
108+
109+
return Item.get(Item.EGG, 0, 1);
110+
}
111+
96112
@Override
97113
public boolean isFeedItem(Item item) {
98114
int id = item.getId();

src/main/java/cn/nukkit/entity/passive/EntityCow.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cn.nukkit.entity.passive;
22

33
import cn.nukkit.Player;
4+
import cn.nukkit.entity.EntityClimateVariant;
45
import cn.nukkit.item.Item;
56
import cn.nukkit.level.format.FullChunk;
67
import cn.nukkit.level.particle.ItemBreakParticle;
@@ -12,7 +13,7 @@
1213
import java.util.ArrayList;
1314
import java.util.List;
1415

15-
public class EntityCow extends EntityWalkingAnimal {
16+
public class EntityCow extends EntityWalkingAnimal implements EntityClimateVariant {
1617

1718
public static final int NETWORK_ID = 11;
1819

@@ -44,8 +45,13 @@ public float getHeight() {
4445
@Override
4546
public void initEntity() {
4647
this.setMaxHealth(10);
47-
4848
super.initEntity();
49+
50+
if (namedTag.contains("variant")) {
51+
setVariant(Variant.get(namedTag.getString("variant")));
52+
} else {
53+
setVariant(getBiomeVariant(getLevel().getBiomeId(getFloorX(), getFloorZ())));
54+
}
4955
}
5056

5157
@Override

src/main/java/cn/nukkit/entity/passive/EntityPig.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22

33
import cn.nukkit.Player;
44
import cn.nukkit.Server;
5-
import cn.nukkit.entity.Attribute;
6-
import cn.nukkit.entity.Entity;
7-
import cn.nukkit.entity.EntityControllable;
8-
import cn.nukkit.entity.EntityRideable;
5+
import cn.nukkit.entity.*;
96
import cn.nukkit.entity.data.FloatEntityData;
107
import cn.nukkit.entity.data.Vector3fEntityData;
118
import cn.nukkit.entity.mob.EntityZombiePigman;
@@ -28,7 +25,7 @@
2825

2926
import static cn.nukkit.network.protocol.SetEntityLinkPacket.TYPE_RIDE;
3027

31-
public class EntityPig extends EntityWalkingAnimal implements EntityRideable, EntityControllable {
28+
public class EntityPig extends EntityWalkingAnimal implements EntityRideable, EntityControllable, EntityClimateVariant {
3229

3330
public static final int NETWORK_ID = 12;
3431

@@ -62,9 +59,14 @@ public float getHeight() {
6259
@Override
6360
public void initEntity() {
6461
this.setMaxHealth(10);
65-
6662
super.initEntity();
6763

64+
if (namedTag.contains("variant")) {
65+
setVariant(Variant.get(namedTag.getString("variant")));
66+
} else {
67+
setVariant(getBiomeVariant(getLevel().getBiomeId(getFloorX(), getFloorZ())));
68+
}
69+
6870
if (this.namedTag.contains("Saddle")) {
6971
this.setSaddled(this.namedTag.getBoolean("Saddle"));
7072
}

src/main/java/cn/nukkit/entity/projectile/EntityEgg.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import cn.nukkit.Server;
44
import cn.nukkit.entity.Entity;
5+
import cn.nukkit.entity.EntityClimateVariant;
56
import cn.nukkit.entity.passive.EntityChicken;
67
import cn.nukkit.event.entity.CreatureSpawnEvent;
78
import cn.nukkit.item.ItemEgg;
@@ -15,7 +16,7 @@
1516
* @author MagicDroidX
1617
* Nukkit Project
1718
*/
18-
public class EntityEgg extends EntityProjectile {
19+
public class EntityEgg extends EntityProjectile implements EntityClimateVariant {
1920

2021
public static final int NETWORK_ID = 82;
2122

@@ -57,6 +58,16 @@ public EntityEgg(FullChunk chunk, CompoundTag nbt, Entity shootingEntity) {
5758
super(chunk, nbt, shootingEntity);
5859
}
5960

61+
@Override
62+
protected void initEntity() {
63+
super.initEntity();
64+
if (namedTag.containsString("variant")) {
65+
setVariant(EntityClimateVariant.Variant.get(namedTag.getString("variant")));
66+
} else {
67+
setVariant(EntityClimateVariant.Variant.TEMPERATE);
68+
}
69+
}
70+
6071
@Override
6172
public boolean onUpdate(int currentTick) {
6273
if (this.closed) {
@@ -80,7 +91,14 @@ public boolean onUpdate(int currentTick) {
8091
}
8192

8293
EntityChicken entity = (EntityChicken) Entity.createEntity("Chicken", spawnPos);
94+
8395
if (entity != null) {
96+
if (namedTag.containsString("variant")) {
97+
entity.setVariant(EntityClimateVariant.Variant.get(namedTag.getString("variant")));
98+
} else {
99+
entity.setVariant(EntityClimateVariant.Variant.TEMPERATE);
100+
}
101+
84102
entity.spawnToAll();
85103
entity.setBaby(true);
86104
}

src/main/java/cn/nukkit/item/Item.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,8 @@ public static void init() {
466466
registerNamespacedIdItem(ItemBannerPatternFlow.class);
467467
registerNamespacedIdItem(ItemBannerPatternGuster.class);
468468
registerNamespacedIdItem(ItemOminousBottle.class);
469+
registerNamespacedIdItem(ItemBlueEgg.class);
470+
registerNamespacedIdItem(ItemBrownEgg.class);
469471

470472

471473
// 添加原版物品到NAMESPACED_ID_ITEM

0 commit comments

Comments
 (0)