Skip to content

Commit b50c558

Browse files
committed
Implement serialization framework.
1 parent d51c5a2 commit b50c558

24 files changed

+2090
-0
lines changed

Common/src/main/java/net/darkhax/bookshelf/api/serialization/ISerializer.java

Lines changed: 451 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package net.darkhax.bookshelf.api.serialization;
2+
3+
public class NBTParseException extends RuntimeException {
4+
5+
public NBTParseException(String msg) {
6+
7+
super(msg);
8+
}
9+
10+
public NBTParseException(String msg, Throwable cause) {
11+
12+
super(msg, cause);
13+
}
14+
15+
public NBTParseException(Throwable cause) {
16+
17+
super(cause);
18+
}
19+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package net.darkhax.bookshelf.api.serialization;
2+
3+
import com.google.gson.JsonElement;
4+
import com.google.gson.JsonObject;
5+
import com.google.gson.JsonParseException;
6+
import net.minecraft.nbt.Tag;
7+
import net.minecraft.network.FriendlyByteBuf;
8+
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
9+
10+
import java.util.Optional;
11+
import java.util.UUID;
12+
13+
public class SerializerAttributeModifier implements ISerializer<AttributeModifier> {
14+
15+
public static final SerializerAttributeModifier SERIALIZER = new SerializerAttributeModifier();
16+
17+
private SerializerAttributeModifier() {
18+
19+
}
20+
21+
@Override
22+
public AttributeModifier fromJSON(JsonElement json) {
23+
24+
if (json instanceof JsonObject obj) {
25+
26+
final Optional<UUID> id = Serializers.UUID.fromJSONOptional(obj, "uuid");
27+
final String name = Serializers.STRING.fromJSON(obj, "name");
28+
final double amount = Serializers.DOUBLE.fromJSON(obj, "amount");
29+
final AttributeModifier.Operation operation = Serializers.ATTRIBUTE_OPERATION.fromJSON(obj, "operation");
30+
31+
if (id.isPresent()) {
32+
33+
return new AttributeModifier(id.get(), name, amount, operation);
34+
}
35+
36+
return new AttributeModifier(name, amount, operation);
37+
}
38+
39+
throw new JsonParseException("Expected JSON object.");
40+
}
41+
42+
@Override
43+
public JsonElement toJSON(AttributeModifier toWrite) {
44+
45+
final JsonObject json = new JsonObject();
46+
json.add("uuid", Serializers.UUID.toJSON(toWrite.getId()));
47+
json.add("name", Serializers.STRING.toJSON(toWrite.getName()));
48+
json.add("amount", Serializers.DOUBLE.toJSON(toWrite.getAmount()));
49+
json.add("operation", Serializers.ATTRIBUTE_OPERATION.toJSON(toWrite.getOperation()));
50+
return json;
51+
}
52+
53+
@Override
54+
public AttributeModifier fromByteBuf(FriendlyByteBuf buffer) {
55+
56+
final UUID uuid = Serializers.UUID.fromByteBuf(buffer);
57+
final String name = Serializers.STRING.fromByteBuf(buffer);
58+
final double amount = Serializers.DOUBLE.fromByteBuf(buffer);
59+
final AttributeModifier.Operation operation = Serializers.ATTRIBUTE_OPERATION.fromByteBuf(buffer);
60+
return new AttributeModifier(uuid, name, amount, operation);
61+
}
62+
63+
@Override
64+
public void toByteBuf(FriendlyByteBuf buffer, AttributeModifier toWrite) {
65+
66+
Serializers.UUID.toByteBuf(buffer, toWrite.getId());
67+
Serializers.STRING.toByteBuf(buffer, toWrite.getName());
68+
Serializers.DOUBLE.toByteBuf(buffer, toWrite.getAmount());
69+
Serializers.ATTRIBUTE_OPERATION.toByteBuf(buffer, toWrite.getOperation());
70+
}
71+
72+
@Override
73+
public Tag toNBT(AttributeModifier toWrite) {
74+
75+
return toWrite.save();
76+
}
77+
78+
@Override
79+
public AttributeModifier fromNBT(Tag nbt) {
80+
81+
return AttributeModifier.load(Serializers.COMPOUND_TAG.fromNBT(nbt));
82+
}
83+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package net.darkhax.bookshelf.api.serialization;
2+
3+
import com.google.gson.JsonArray;
4+
import com.google.gson.JsonElement;
5+
import com.google.gson.JsonObject;
6+
import com.google.gson.JsonParseException;
7+
import com.google.gson.JsonPrimitive;
8+
import net.minecraft.core.BlockPos;
9+
import net.minecraft.nbt.CompoundTag;
10+
import net.minecraft.nbt.ListTag;
11+
import net.minecraft.nbt.NumericTag;
12+
import net.minecraft.nbt.Tag;
13+
import net.minecraft.network.FriendlyByteBuf;
14+
15+
public class SerializerBlockPos implements ISerializer<BlockPos> {
16+
17+
public static final ISerializer<BlockPos> SERIALIZER = new SerializerBlockPos();
18+
19+
@Override
20+
public BlockPos fromJSON(JsonElement json) {
21+
22+
// If it's an object explicitly read each axis.
23+
if (json.isJsonObject()) {
24+
25+
final JsonObject object = json.getAsJsonObject();
26+
final int x = Serializers.INT.fromJSON(object, "x");
27+
final int y = Serializers.INT.fromJSON(object, "y");
28+
final int z = Serializers.INT.fromJSON(object, "z");
29+
return new BlockPos(x, y, z);
30+
}
31+
32+
// If it's an array of 3 elements read in X,Y,Z order.
33+
else if (json.isJsonArray()) {
34+
35+
final JsonArray array = json.getAsJsonArray();
36+
37+
if (array.size() == 3) {
38+
39+
return new BlockPos(array.get(0).getAsInt(), array.get(1).getAsInt(), array.get(2).getAsInt());
40+
}
41+
42+
throw new JsonParseException("Invalid number of args in array. Expected 3 but got " + array.size());
43+
}
44+
45+
// If it's a primitive, try to read it as a long.
46+
else if (json.isJsonPrimitive()) {
47+
48+
final JsonPrimitive primitive = json.getAsJsonPrimitive();
49+
50+
if (primitive.isNumber()) {
51+
52+
return BlockPos.of(json.getAsLong());
53+
}
54+
55+
throw new JsonParseException("Expected JSON primitive to be a number.");
56+
}
57+
58+
throw new JsonParseException("BlockPos data is not in a readable format.");
59+
}
60+
61+
@Override
62+
public JsonElement toJSON(BlockPos toWrite) {
63+
64+
final JsonObject object = new JsonObject();
65+
object.addProperty("x", toWrite.getX());
66+
object.addProperty("y", toWrite.getY());
67+
object.addProperty("z", toWrite.getZ());
68+
return object;
69+
}
70+
71+
@Override
72+
public BlockPos fromByteBuf(FriendlyByteBuf buffer) {
73+
74+
return BlockPos.of(buffer.readLong());
75+
}
76+
77+
@Override
78+
public void toByteBuf(FriendlyByteBuf buffer, BlockPos toWrite) {
79+
80+
buffer.writeLong(toWrite.asLong());
81+
}
82+
83+
@Override
84+
public Tag toNBT(BlockPos toWrite) {
85+
86+
final CompoundTag tag = new CompoundTag();
87+
tag.putInt("x", toWrite.getX());
88+
tag.putInt("y", toWrite.getY());
89+
tag.putInt("z", toWrite.getZ());
90+
return tag;
91+
}
92+
93+
@Override
94+
public BlockPos fromNBT(Tag nbt) {
95+
96+
// If it's a compound explicitly read each axis.
97+
if (nbt instanceof CompoundTag compound) {
98+
99+
final int x = compound.getInt("x");
100+
final int y = compound.getInt("y");
101+
final int z = compound.getInt("z");
102+
return new BlockPos(x, y, z);
103+
}
104+
105+
// If it's a list of 3 elements read in X,Y,Z order.
106+
else if (nbt instanceof ListTag list) {
107+
108+
if (list.size() == 3) {
109+
110+
return new BlockPos(Serializers.INT.fromNBT(list.get(0)), Serializers.INT.fromNBT(list.get(1)), Serializers.INT.fromNBT(list.get(2)));
111+
}
112+
113+
throw new NBTParseException("Invalid number of args in array. Expected 3 but got " + list.size());
114+
}
115+
116+
// If it's a number, try to read it as a long.
117+
else if (nbt instanceof NumericTag numberTag) {
118+
119+
return BlockPos.of(numberTag.getAsLong());
120+
}
121+
122+
throw new NBTParseException("BlockPos data is not in a readable format.");
123+
}
124+
125+
}
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package net.darkhax.bookshelf.api.serialization;
2+
3+
import com.google.gson.JsonElement;
4+
import com.google.gson.JsonObject;
5+
import com.google.gson.JsonParseException;
6+
import com.google.gson.JsonSyntaxException;
7+
import net.darkhax.bookshelf.Bookshelf;
8+
import net.minecraft.core.Registry;
9+
import net.minecraft.nbt.CompoundTag;
10+
import net.minecraft.nbt.NbtUtils;
11+
import net.minecraft.nbt.Tag;
12+
import net.minecraft.network.FriendlyByteBuf;
13+
import net.minecraft.util.GsonHelper;
14+
import net.minecraft.world.level.block.Block;
15+
import net.minecraft.world.level.block.state.BlockState;
16+
import net.minecraft.world.level.block.state.properties.BooleanProperty;
17+
import net.minecraft.world.level.block.state.properties.IntegerProperty;
18+
import net.minecraft.world.level.block.state.properties.Property;
19+
20+
import java.util.Map.Entry;
21+
import java.util.Optional;
22+
23+
public class SerializerBlockState implements ISerializer<BlockState> {
24+
25+
public static final ISerializer<BlockState> SERIALIZER = new SerializerBlockState();
26+
27+
private SerializerBlockState() {
28+
29+
}
30+
31+
@Override
32+
public BlockState fromJSON(JsonElement json) {
33+
34+
if (json.isJsonObject()) {
35+
36+
final JsonObject obj = json.getAsJsonObject();
37+
final Block block = Serializers.BLOCK.fromJSON(obj, "block");
38+
39+
BlockState state = block.defaultBlockState();
40+
41+
if (obj.has("properties")) {
42+
43+
final JsonElement properties = obj.get("properties");
44+
45+
for (final Entry<String, JsonElement> property : properties.getAsJsonObject().entrySet()) {
46+
47+
state = this.readProperty(state, property.getKey(), property.getValue());
48+
}
49+
}
50+
51+
return state;
52+
}
53+
else {
54+
55+
throw new JsonParseException("Expected properties to be an object. Recieved " + GsonHelper.getType(json));
56+
}
57+
}
58+
59+
@Override
60+
public JsonElement toJSON(BlockState toWrite) {
61+
62+
final JsonObject json = new JsonObject();
63+
json.add("block", Serializers.BLOCK.toJSON(toWrite.getBlock()));
64+
65+
final JsonObject properties = new JsonObject();
66+
67+
for (final Property prop : toWrite.getProperties()) {
68+
69+
if (prop instanceof IntegerProperty) {
70+
71+
properties.addProperty(prop.getName(), (int) toWrite.getValue((IntegerProperty) prop));
72+
}
73+
else if (prop instanceof BooleanProperty) {
74+
75+
properties.addProperty(prop.getName(), (boolean) toWrite.getValue((BooleanProperty) prop));
76+
}
77+
else {
78+
79+
properties.addProperty(prop.getName(), prop.getName(toWrite.getValue(prop)));
80+
}
81+
}
82+
83+
json.add("properties", properties);
84+
return json;
85+
}
86+
87+
@Override
88+
public BlockState fromByteBuf(FriendlyByteBuf buffer) {
89+
90+
return Block.stateById(buffer.readInt());
91+
}
92+
93+
@Override
94+
public void toByteBuf(FriendlyByteBuf buffer, BlockState toWrite) {
95+
96+
buffer.writeInt(Block.getId(toWrite));
97+
}
98+
99+
private BlockState readProperty(BlockState state, String propName, JsonElement propValue) {
100+
101+
final Property blockProperty = state.getBlock().getStateDefinition().getProperty(propName);
102+
103+
if (blockProperty != null) {
104+
105+
if (propValue.isJsonPrimitive()) {
106+
107+
final String valueString = propValue.getAsString();
108+
final Optional<Comparable> parsedValue = blockProperty.getValue(valueString);
109+
110+
if (parsedValue.isPresent()) {
111+
112+
try {
113+
114+
return state.setValue(blockProperty, parsedValue.get());
115+
}
116+
117+
catch (final Exception e) {
118+
119+
Bookshelf.LOG.error("Failed to update state for block {}. The mod that adds this block may have an issue.", Registry.BLOCK.getId(state.getBlock()));
120+
Bookshelf.LOG.catching(e);
121+
throw e;
122+
}
123+
}
124+
else {
125+
126+
throw new JsonSyntaxException("The property " + propName + " with value " + valueString + " coul not be parsed!");
127+
}
128+
}
129+
else {
130+
131+
throw new JsonSyntaxException("Expected property value for " + propName + " to be primitive string. Got " + GsonHelper.getType(propValue));
132+
}
133+
}
134+
else {
135+
136+
throw new JsonSyntaxException("The property " + propName + " is not valid for block " + Registry.BLOCK.getId(state.getBlock()));
137+
}
138+
}
139+
140+
@Override
141+
public Tag toNBT(BlockState toWrite) {
142+
143+
return NbtUtils.writeBlockState(toWrite);
144+
}
145+
146+
@Override
147+
public BlockState fromNBT(Tag nbt) {
148+
149+
if (nbt instanceof CompoundTag compound) {
150+
151+
return NbtUtils.readBlockState(compound);
152+
}
153+
154+
throw new NBTParseException("Expected NBT to be a compound tag. Class was " + nbt.getClass() + " with ID " + nbt.getId() + " instead.");
155+
}
156+
}

0 commit comments

Comments
 (0)