Implement networking, reimplement pega-reach

This commit is contained in:
Sollace 2020-04-23 23:44:31 +02:00
parent 2536872aad
commit 5708f9c80f
36 changed files with 367 additions and 608 deletions

View file

@ -1,23 +0,0 @@
package com.minelittlepony.jumpingcastle.api;
import java.util.UUID;
import javax.annotation.Nullable;
import com.minelittlepony.jumpingcastle.api.payload.BinaryPayload;
/**
* Implementor for a Jumping Castle API bus.
*/
public interface Bus {
void sendToServer(String channel, long id, Message message, Target target);
void sendToClient(String channel, long id, Message message, UUID playerId);
void sendToClient(UUID playerId, BinaryPayload forwarded);
@Nullable
Object getMinecraftServer();
Server getServer();
}

View file

@ -1,60 +0,0 @@
package com.minelittlepony.jumpingcastle.api;
import java.util.UUID;
/**
* A channel for sending and recieving messages.
*/
public interface Channel {
/**
* Registers a handler for a specific message type transmitted over this channel.
*
* @param messageType The message type being recieved.
* @param handler A handler instance to handle the message.
*/
<T extends Message> Channel listenFor(Class<T> messageType, Message.Handler<T> handler);
/**
* Registers a handler for a specific message type transmitted over this channel.
*
* @param messageType The message type being recieved.
*/
<T extends Message & Message.Handler<T>> Channel listenFor(Class<T> messageType);
/**
* Gets the minecraft server
*/
<T> T getServer();
/**
* Sends a message over this channel. By default targets all other clients listening on this channel.
*
* @param message The message to send.
*/
default Channel send(Message message) {
return send(message, Target.CLIENTS);
}
/**
* Sends a message over this channel.
*
* @param message The message to send.
* @param target Recipients that must handle this message (clients, server, or both)
*/
Channel send(Message message, Target target);
/**
* Sends a message back. Use this if you're a server.
*
* @param message The message to send.
* @param recipient Recipient that must handle this message
*/
Channel respond(Message message, UUID recipient);
/**
* Sends a message back to all clients. Use this if you're a server.
*
* @param message The message to send.
*/
Channel broadcast(Message message);
}

View file

@ -1,28 +0,0 @@
package com.minelittlepony.jumpingcastle.api;
/**
* Jumping Castle main interface.
* <p>
*{@code
* JumpingCastle.listen("My Channel").consume(MyMessage.class, (msg, channel) -> {
* ...
* });
*
*/
public interface JumpingCastle {
/**
* Gets or creates a new channel indexed by the given identifier.
*
* @param channelName The channel name
*
* @return An instance of IChannel.
*/
static Channel subscribeTo(String channelName, Client clientHandler) {
return null;// TODO: JumpingFabric
}
@FunctionalInterface
public interface Client {
void connectionEstablished();
}
}

View file

@ -1,45 +0,0 @@
package com.minelittlepony.jumpingcastle.api;
import com.minelittlepony.jumpingcastle.api.payload.Serializable;
/**
* A message for communicating over a channel.
* Fields marked with @Expose are automatically serialized to the output packet stream
* and will be made available on the far end of the pipe.
*
* Override the read and write methods to customise this behaviour.
*
*/
public interface Message extends Serializable<Message> {
/**
* Gets a unique identifier to represent a packet over channel communications.
*/
static long identifier(Class<? extends Message> cls) {
return cls.getCanonicalName().hashCode();
}
/**
* Gets a unique identifier to represent this packet over channel communications.
*/
default long identifier() {
return identifier(getClass());
}
/**
* Handler interface for incoming messages.
*
* @param <T> The type of message to handle.
*/
@FunctionalInterface
public interface Handler<T extends Message> {
/**
* Called when a new message is received.
*
* @param message The message received.
*
* @param channel The channel used to deliver the message.
*/
void onPayload(T message, Channel channel);
}
}

View file

@ -1,7 +0,0 @@
package com.minelittlepony.jumpingcastle.api;
import java.util.UUID;
public interface Server {
void stopTrackingPlayer(UUID playerId);
}

View file

@ -1,43 +0,0 @@
package com.minelittlepony.jumpingcastle.api;
/**
* Targeting method to control who receives a message when sent.
* Senders choose whether a message is received on other clients, just the server, or both.
*/
public enum Target {
/**
* Message is ignored by the server and forwarded to connected clients.
*/
CLIENTS(true, false),
/**
* Only the server should process this message. Clients ignore it.
*/
SERVER(false, true),
/**
* The server should process this message and forward it to all available clients.
*/
SERVER_AND_CLIENTS(true, true);
private final boolean clients;
private final boolean servers;
Target(boolean clients, boolean servers) {
this.clients = clients;
this.servers = servers;
}
/**
* True if messages with this targeting must be processed by the client.
*/
public boolean isClients() {
return clients;
}
/**
* True if messages with this targeting must be processed by the server.
*/
public boolean isServers() {
return servers;
}
}

View file

@ -1,63 +0,0 @@
package com.minelittlepony.jumpingcastle.api.payload;
import javax.annotation.Nullable;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
public interface BinaryPayload {
@Nullable
static BinaryPayload of(Object buffer) {
if (buffer instanceof ByteBuf) {
return ByteBufBinaryPayload.of((ByteBuf)buffer);
}
return null;
}
static BinaryPayload create() {
return of(Unpooled.buffer());
}
<T> T buff();
byte[] bytes();
byte[] readToEnd();
long readLong();
BinaryPayload writeLong(long l);
int readInt();
BinaryPayload writeInt(int b);
byte readByte();
BinaryPayload writeByte(byte b);
String readString();
BinaryPayload writeString(String s);
byte[] readBytes(int len);
BinaryPayload writeBytes(byte[] bytes);
BinaryPayload reverse();
@SuppressWarnings("unchecked")
default <T extends Serializable<? super T>> T readBinary(Class<T> type) {
try {
return (T)type.newInstance().read(this);
} catch (InstantiationException | IllegalAccessException e) {
return null;
}
}
default <T extends Serializable<T>> BinaryPayload writeBinary(T message) {
message.write(this);
return this;
}
}

View file

@ -1,89 +0,0 @@
package com.minelittlepony.jumpingcastle.api.payload;
import java.nio.charset.StandardCharsets;
import io.netty.buffer.ByteBuf;
interface ByteBufBinaryPayload extends BinaryPayload {
static BinaryPayload of(ByteBuf buff) {
return (ByteBufBinaryPayload)(() -> buff);
}
@SuppressWarnings("unchecked")
ByteBuf buff();
@Override
default String readString() {
return buff().readCharSequence(readInt(), StandardCharsets.UTF_8).toString();
}
@Override
default int readInt() {
return buff().readInt();
}
@Override
default BinaryPayload writeInt(int b) {
buff().writeInt(b);
return this;
}
@Override
default byte readByte() {
return buff().readByte();
}
@Override
default BinaryPayload writeByte(byte b) {
buff().writeByte(b);
return this;
}
default byte[] bytes() {
return buff().array();
}
default byte[] readToEnd() {
byte[] bytes = new byte[buff().writerIndex() - buff().readerIndex()];
buff().readBytes(bytes);
return bytes;
}
@Override
default long readLong() {
return buff().readLong();
}
@Override
default BinaryPayload writeLong(long l) {
buff().writeLong(l);
return this;
}
@Override
default BinaryPayload reverse() {
buff().readerIndex(0);
return this;
}
@Override
default byte[] readBytes(int len) {
byte[] data = new byte[len];
buff().readBytes(data);
return data;
}
@Override
default BinaryPayload writeBytes(byte[] bytes) {
buff().writeBytes(bytes);
return this;
}
@Override
default BinaryPayload writeString(String s) {
buff().writeInt(s.getBytes(StandardCharsets.UTF_8).length);
buff().writeCharSequence(s, StandardCharsets.UTF_8);
return this;
}
}

View file

@ -1,19 +0,0 @@
package com.minelittlepony.jumpingcastle.api.payload;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class JsonSerializer {
private static final Gson READER_GSON = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
public static <T> T read(BinaryPayload payload, Class<T> type) {
return (T)READER_GSON.fromJson(payload.readString(), type);
}
public static <T> void write(BinaryPayload payload, T object) {
payload.writeString(READER_GSON.toJson(object));
}
}

View file

@ -1,19 +0,0 @@
package com.minelittlepony.jumpingcastle.api.payload;
public interface Serializable<T extends Serializable<T>> {
/**
* Reads the contents of this message from a networking payload.
*/
@SuppressWarnings("unchecked")
default T read(BinaryPayload payload) {
return (T)JsonSerializer.read(payload, getClass());
}
/**
* Writes the contents of this message to a networking payload.
*/
default void write(BinaryPayload payload) {
JsonSerializer.write(payload, this);
}
}

View file

@ -16,7 +16,7 @@ import net.minecraft.world.World;
@Deprecated
public class CustomDrops {
// TODO: loot table
// XXX: loot table
public void addAuxiliaryDrops(World world, BlockState state, BlockPos pos, List<ItemStack> drops, int fortune) {
Block block = state.getBlock();

View file

@ -18,7 +18,7 @@ import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
public final class TreeType {
// TODO: move to datapack
// XXX: move to datapack
private static final Set<TreeType> REGISTRY = new HashSet<>();
public static final TreeType NONE = new TreeType("none", new Weighted<Supplier<ItemStack>>());

View file

@ -8,8 +8,6 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.minelittlepony.common.util.GamePaths;
import com.minelittlepony.jumpingcastle.api.Channel;
import com.minelittlepony.jumpingcastle.api.JumpingCastle;
import com.minelittlepony.unicopia.ability.Abilities;
import com.minelittlepony.unicopia.advancement.BOHDeathCriterion;
import com.minelittlepony.unicopia.block.UBlocks;
@ -17,39 +15,28 @@ import com.minelittlepony.unicopia.command.Commands;
import com.minelittlepony.unicopia.container.UContainers;
import com.minelittlepony.unicopia.enchanting.Pages;
import com.minelittlepony.unicopia.enchanting.recipe.AffineIngredients;
import com.minelittlepony.unicopia.enchanting.recipe.URecipes;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.mixin.CriterionsRegistry;
import com.minelittlepony.unicopia.network.MsgPlayerAbility;
import com.minelittlepony.unicopia.network.MsgPlayerCapabilities;
import com.minelittlepony.unicopia.network.MsgRequestCapabilities;
import com.minelittlepony.unicopia.network.Channel;
import com.minelittlepony.unicopia.structure.UStructures;
public class Unicopia implements ModInitializer {
public static final String MODID = "unicopia";
public static final Logger LOGGER = LogManager.getLogger();
private static Channel channel;
public static Channel getConnection() {
return channel;
}
@Override
public void onInitialize() {
Config.init(GamePaths.getConfigDirectory());
channel = JumpingCastle.subscribeTo(MODID, () -> {})
.listenFor(MsgRequestCapabilities.class)
.listenFor(MsgPlayerCapabilities.class)
.listenFor(MsgPlayerAbility.class);
Channel.bootstrap();
UTags.bootstrap();
Commands.bootstrap();
UBlocks.bootstrap();
UItems.bootstrap();
UContainers.bootstrap();
UStructures.bootstrap();
URecipes.bootstrap();
Abilities.getInstance().init();
CriterionsRegistry.register(BOHDeathCriterion.INSTANCE);

View file

@ -50,7 +50,7 @@ public class UnicornCastingAbility implements Ability<Ability.Hit> {
@Override
public void apply(Pony player, Hit data) {
// TODO: A way for the player to select which effect they want
// XXX: A way for the player to select which effect they want
if (player.getEffect() instanceof ShieldSpell) {
player.setEffect(null);
} else {

View file

@ -6,11 +6,9 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.minelittlepony.common.event.ClientReadyCallback;
import com.minelittlepony.jumpingcastle.api.Target;
import com.minelittlepony.unicopia.Config;
import com.minelittlepony.unicopia.InteractionManager;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.Abilities;
import com.minelittlepony.unicopia.block.UBlocks;
import com.minelittlepony.unicopia.container.SpellbookResultSlot;
@ -19,6 +17,7 @@ import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.magic.spell.SpellRegistry;
import com.minelittlepony.unicopia.mixin.client.DefaultTexturesRegistry;
import com.minelittlepony.unicopia.network.Channel;
import com.minelittlepony.unicopia.network.MsgRequestCapabilities;
import com.minelittlepony.unicopia.util.dummy.DummyClientPlayerEntity;
import com.mojang.authlib.GameProfile;
@ -84,7 +83,7 @@ public class UnicopiaClient extends InteractionManager implements ClientModIniti
if (newRace != clientPlayerRace) {
clientPlayerRace = newRace;
Unicopia.getConnection().send(new MsgRequestCapabilities(player, clientPlayerRace), Target.SERVER);
Channel.REQUEST_CAPABILITIES.send(new MsgRequestCapabilities(player, clientPlayerRace));
}
}

View file

@ -5,7 +5,7 @@ import com.minelittlepony.unicopia.entity.player.Pony;
import net.minecraft.client.MinecraftClient;
@Deprecated
// TODO: forge events
// XXX: hud render events
class HudHooks {
public static void beforePreRenderHud() {
MinecraftClient client = MinecraftClient.getInstance();

View file

@ -5,6 +5,7 @@ import javax.annotation.Nonnull;
import com.minelittlepony.unicopia.AwaitTickQueue;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.enchanting.IPageUnlockListener;
import com.minelittlepony.unicopia.enchanting.recipe.URecipes;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.magic.spell.SpellRegistry;
@ -16,7 +17,6 @@ import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.recipe.Recipe;
import net.minecraft.recipe.RecipeType;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
@ -73,8 +73,7 @@ public class SpellBookContainer extends Container {
ItemStack current = craftResult.getInvStack(0);
if (!current.isEmpty()) {
// TODO: URecipeType.SPELL_BOOK
ItemStack crafted = player.world.getRecipeManager().getFirstMatch(RecipeType.CRAFTING, craftMatrix, worldObj)
ItemStack crafted = player.world.getRecipeManager().getFirstMatch(URecipes.SPELL_BOOK, craftMatrix, worldObj)
.map(Recipe::getOutput)
.orElse(ItemStack.EMPTY);

View file

@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.container;
import com.minelittlepony.unicopia.enchanting.IPageUnlockListener;
import com.minelittlepony.unicopia.enchanting.SpellCraftingEvent;
import com.minelittlepony.unicopia.enchanting.recipe.URecipes;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.MagicGemItem;
import com.minelittlepony.unicopia.magic.spell.SpellRegistry;
@ -12,7 +13,6 @@ import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.MusicDiscItem;
import net.minecraft.recipe.RecipeType;
import net.minecraft.util.DefaultedList;
import net.minecraft.util.Identifier;
@ -49,8 +49,7 @@ public class SpellbookResultSlot extends SpellBookContainer.SpellbookSlot {
ItemStack current = craftMatrix.getCraftResultMatrix().getInvStack(0);
craftMatrix.getCraftResultMatrix().setInvStack(0, stack);
// TODO: URecipeType.SPELL_BOOK
DefaultedList<ItemStack> remaining = player.world.getRecipeManager().getRemainingStacks(RecipeType.CRAFTING, craftMatrix, player.world);
DefaultedList<ItemStack> remaining = player.world.getRecipeManager().getRemainingStacks(URecipes.SPELL_BOOK, craftMatrix, player.world);
craftMatrix.getCraftResultMatrix().setInvStack(0, current);

View file

@ -1,7 +1,7 @@
package com.minelittlepony.unicopia.ducks;
@Deprecated
// TODO: 1.16 Use the vanilla BlockTag once mojang adds it
// XXX: 1.16 Use the vanilla BlockTag once mojang adds it
public interface Climbable {
}

View file

@ -2,29 +2,18 @@ package com.minelittlepony.unicopia.enchanting.recipe;
import com.google.gson.JsonObject;
import net.minecraft.inventory.CraftingInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.recipe.Recipe;
import net.minecraft.recipe.RecipeSerializer;
import net.minecraft.recipe.RecipeType;
import net.minecraft.util.DefaultedList;
import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
public class SpecialRecipe extends AbstractSpecialRecipe {
private final SpellIngredient output;
public static Recipe<CraftingInventory> deserialize(JsonObject json) {
return new SpecialRecipe(null,
SpellIngredient.single(json.get("input").getAsJsonObject()),
SpellIngredient.single(json.get("output").getAsJsonObject()),
SpellIngredient.multiple(json)
);
}
public SpecialRecipe(Identifier id, SpellIngredient input, SpellIngredient output, DefaultedList<SpellIngredient> ingredients) {
super(id, input, ingredients);
this.output = output;
}
@ -35,13 +24,45 @@ public class SpecialRecipe extends AbstractSpecialRecipe {
@Override
public RecipeSerializer<?> getSerializer() {
// TODO Auto-generated method stub
return null;
return URecipes.SPECIAL_SERIALIZER;
}
@Override
public RecipeType<?> getType() {
// TODO Auto-generated method stub
return null;
return URecipes.SPELL_BOOK;
}
public static class Serializer implements RecipeSerializer<SpecialRecipe> {
@Override
public SpecialRecipe read(Identifier id, JsonObject json) {
return new SpecialRecipe(id,
SpellIngredient.single(json.get("input").getAsJsonObject()),
SpellIngredient.single(json.get("output").getAsJsonObject()),
SpellIngredient.multiple(json)
);
}
@Override
public SpecialRecipe read(Identifier id, PacketByteBuf buf) {
SpellIngredient input = SpellIngredient.SERIALIZER.read(buf);
SpellIngredient output = SpellIngredient.SERIALIZER.read(buf);
int length = buf.readInt();
DefaultedList<SpellIngredient> ingredients = DefaultedList.copyOf(SpellIngredient.EMPTY);
while (ingredients.size() < length) {
ingredients.add(SpellIngredient.SERIALIZER.read(buf));
}
return new SpecialRecipe(id, input, output, ingredients);
}
@Override
public void write(PacketByteBuf buf, SpecialRecipe recipe) {
recipe.getSpellItem().write(buf);
recipe.output.write(buf);
DefaultedList<SpellIngredient> ingredients = recipe.getSpellIngredients();
buf.writeInt(ingredients.size());
ingredients.forEach(i -> i.write(buf));
}
}
}

View file

@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.enchanting.recipe;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.google.gson.JsonElement;
@ -20,6 +21,7 @@ public interface SpellIngredient {
map.put("single", SingleSpellIngredient.SERIALIZER);
map.put("affine", AffineIngredient.SERIALIZER);
});
Map<Serializer<? extends SpellIngredient>, String> IDS = SERIALIZERS.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
Serializer<SpellIngredient> SERIALIZER = new Serializer<SpellIngredient>() {
@Override
@ -40,7 +42,7 @@ public interface SpellIngredient {
@Override
public void write(PacketByteBuf buff, SpellIngredient recipe) {
buff.writeByte(recipe instanceof SingleSpellIngredient ? 0 : 1);
buff.writeString(IDS.get(recipe.getSerializer()));
recipe.write(buff);
}
};

View file

@ -45,16 +45,15 @@ public class SpellRecipe extends AbstractSpecialRecipe {
@Override
public RecipeSerializer<?> getSerializer() {
return null;
return URecipes.SPELL_SERIALIZER;
}
@Override
public RecipeType<?> getType() {
return null;
return URecipes.SPELL_BOOK;
}
public static class Serializer implements RecipeSerializer<SpellRecipe> {
@Override
public SpellRecipe read(Identifier id, JsonObject json) {
JsonObject resultJson = json.get("result").getAsJsonObject();
@ -93,4 +92,5 @@ public class SpellRecipe extends AbstractSpecialRecipe {
ingredients.forEach(i -> i.write(buff));
}
}
}

View file

@ -0,0 +1,30 @@
package com.minelittlepony.unicopia.enchanting.recipe;
import net.minecraft.recipe.Recipe;
import net.minecraft.recipe.RecipeSerializer;
import net.minecraft.recipe.RecipeType;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
public interface URecipes {
RecipeType<SpellRecipe> SPELL_BOOK = register("spell_book");
RecipeSerializer<SpecialRecipe> SPECIAL_SERIALIZER = register("special_spell", new SpecialRecipe.Serializer());
RecipeSerializer<SpellRecipe> SPELL_SERIALIZER = register("spell", new SpellRecipe.Serializer());
static <T extends Recipe<?>> RecipeType<T> register(final String id) {
return Registry.register(Registry.RECIPE_TYPE, new Identifier("unicopia", id), new RecipeType<T>() {
@Override
public String toString() {
return id;
}
});
}
static <S extends RecipeSerializer<T>, T extends Recipe<?>> S register(String id, S serializer) {
return Registry.register(Registry.RECIPE_SERIALIZER, new Identifier("unicopia", id), serializer);
}
static void bootstrap() {}
}

View file

@ -1,18 +1,17 @@
package com.minelittlepony.unicopia.entity;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.network.Channel;
import com.minelittlepony.unicopia.network.MsgSpawnRainbow;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.packet.EntitySpawnGlobalS2CPacket;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityDimensions;
import net.minecraft.entity.EntityPose;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LightningEntity;
import net.minecraft.entity.SpawnType;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.NetworkThreadUtils;
import net.minecraft.network.Packet;
import net.minecraft.network.listener.ClientPlayPacketListener;
import net.minecraft.sound.SoundCategory;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.MathHelper;
@ -26,7 +25,7 @@ public class RainbowEntity extends Entity implements InAnimate {
private int ticksAlive;
private double radius;
private final double radius;
public static final int RAINBOW_MAX_SIZE = 180;
public static final int RAINBOW_MIN_SIZE = 50;
@ -49,8 +48,7 @@ public class RainbowEntity extends Entity implements InAnimate {
ignoreCameraFrustum = true;
//width = (float)radius;
//height = width;
calculateDimensions();
}
@Override
@ -69,6 +67,11 @@ public class RainbowEntity extends Entity implements InAnimate {
));
}
@Override
public EntityDimensions getDimensions(EntityPose pose) {
return EntityDimensions.changing((float)getRadius(), (float)getRadius());
}
@Override
public SoundCategory getSoundCategory() {
return SoundCategory.WEATHER;
@ -119,7 +122,7 @@ public class RainbowEntity extends Entity implements InAnimate {
@Override
public Packet<?> createSpawnPacket() {
return new SpawnPacket(this);
return Channel.SPAWN_RAINBOW.toPacket(new MsgSpawnRainbow(this));
}
public static class Spawner extends MobEntity {
@ -159,27 +162,4 @@ public class RainbowEntity extends Entity implements InAnimate {
world.spawnEntity(rainbow);
}
}
static class SpawnPacket extends EntitySpawnGlobalS2CPacket {
public SpawnPacket(RainbowEntity entity) {
super(entity);
}
@Override
public void apply(ClientPlayPacketListener listener) {
// TODO: Packet needs to be registered, and handling separated
MinecraftClient client = MinecraftClient.getInstance();
NetworkThreadUtils.forceMainThread(this, listener, client);
double x = getX();
double y = getY();
double z = getZ();
LightningEntity entity = new LightningEntity(client.world, x, y, z, false);
entity.updateTrackedPosition(x, y, z);
entity.yaw = 0;
entity.pitch = 0;
entity.setEntityId(getId());
client.world.addLightning(entity);
}
}
}

View file

@ -1,15 +1,13 @@
package com.minelittlepony.unicopia.entity.player;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.minelittlepony.jumpingcastle.api.Target;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.AbilityReceiver;
import com.minelittlepony.unicopia.ability.Ability;
import com.minelittlepony.unicopia.ability.Abilities;
import com.minelittlepony.unicopia.entity.Updatable;
import com.minelittlepony.unicopia.network.MsgPlayerAbility;
import com.minelittlepony.unicopia.network.Channel;
import com.minelittlepony.unicopia.util.NbtSerialisable;
import net.minecraft.nbt.CompoundTag;
@ -112,7 +110,11 @@ class AbilityDelegate implements AbilityReceiver, Updatable, NbtSerialisable {
cooldown = ability.getCooldownTime(player);
if (player.isClientPlayer()) {
if (!activateAbility(ability)) {
Ability.IData data = ability.tryActivate(player);
if (data != null) {
Channel.PLAYER_ABILITY.send(new MsgPlayerAbility(ability, data));
} else {
cooldown = 0;
}
}
@ -150,18 +152,4 @@ class AbilityDelegate implements AbilityReceiver, Updatable, NbtSerialisable {
.ifPresent(p -> activeAbility = p);
}
}
/**
* Attempts to activate the current stored ability.
* Returns true if the ability suceeded, otherwise false.
*/
protected boolean activateAbility(@Nonnull Ability<?> ability) {
Ability.IData data = ability.tryActivate(player);
if (data != null) {
Unicopia.getConnection().send(new MsgPlayerAbility(player.getOwner(), ability, data), Target.SERVER);
}
return data != null;
}
}

View file

@ -15,6 +15,7 @@ import com.minelittlepony.unicopia.util.NbtSerialisable;
import com.minelittlepony.unicopia.util.MutableVector;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityPose;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.sound.SoundCategory;
@ -41,6 +42,8 @@ public class GravityDelegate implements Updatable, FlightControl, NbtSerialisabl
private float gravity = 0;
private float eyeHeight;
public GravityDelegate(Pony player) {
this.player = player;
}
@ -65,6 +68,10 @@ public class GravityDelegate implements Updatable, FlightControl, NbtSerialisabl
return Math.sqrt(getHorizontalMotion(player.getOwner())) > 0.4F;
}
public float getEyeHeight() {
return eyeHeight;
}
@Override
public float getTargetEyeHeight(Pony player) {
if (player.hasEffect()) {
@ -81,7 +88,8 @@ public class GravityDelegate implements Updatable, FlightControl, NbtSerialisabl
return 0.5F;
}
return player.getOwner().getStandingEyeHeight();
EntityPose pose = player.getOwner().getPose();
return player.getOwner().getActiveEyeHeight(pose, player.getOwner().getDimensions(pose));
}
@Override
@ -163,7 +171,8 @@ public class GravityDelegate implements Updatable, FlightControl, NbtSerialisabl
}
float bodyHeight = getTargetBodyHeight(player);
float eyeHeight = getTargetEyeHeight(player);
eyeHeight = 0;
eyeHeight = getTargetEyeHeight(player);
if (gravity < 0) {
eyeHeight = bodyHeight - eyeHeight;
@ -176,8 +185,6 @@ public class GravityDelegate implements Updatable, FlightControl, NbtSerialisabl
((MixinEntity)entity).setSize(entity.getWidth(), bodyHeight);
// TODO: Change eye height
//entity.eyeHeight = eyeHeight;
if (gravity < 0) {
if (entity.isSneaking()) {

View file

@ -6,6 +6,7 @@ import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.container.HeavyInventory;
import com.minelittlepony.unicopia.mixin.Walker;
import net.minecraft.entity.attribute.ClampedEntityAttribute;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeInstance;
import net.minecraft.entity.attribute.EntityAttributeModifier;
@ -13,14 +14,16 @@ import net.minecraft.entity.attribute.EntityAttributeModifier.Operation;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.entity.player.PlayerEntity;
public class PlayerAttributes {
class PlayerAttributes {
static final EntityAttribute EXTENDED_REACH_DISTANCE = new ClampedEntityAttribute(null, "player.reachDistance", 0, 0, 10);
private static final EntityAttributeModifier EARTH_PONY_STRENGTH =
new EntityAttributeModifier(UUID.fromString("777a5505-521e-480b-b9d5-6ea54f259564"), "Earth Pony Strength", 0.6, Operation.MULTIPLY_TOTAL);
private static final EntityAttributeModifier PEGASUS_SPEED =
new EntityAttributeModifier(UUID.fromString("9e2699fc-3b8d-4f71-9d2d-fb92ee19b4f7"), "Pegasus Speed", 0.2, Operation.MULTIPLY_TOTAL);
//private static final EntityAttributeModifier PEGASUS_REACH =
// new EntityAttributeModifier(UUID.fromString("707b50a8-03e8-40f4-8553-ecf67025fd6d"), "Pegasus Reach", 1.5, Operation.ADDITION);
private static final EntityAttributeModifier PEGASUS_REACH =
new EntityAttributeModifier(UUID.fromString("707b50a8-03e8-40f4-8553-ecf67025fd6d"), "Pegasus Reach", 1.5, Operation.ADDITION);
private double loadStrength = 0;
@ -29,14 +32,15 @@ public class PlayerAttributes {
((Walker)entity.abilities).setWalkSpeed(0.1F - (float)(loadStrength / 100000));
applyAttribute(entity, EntityAttributes.ATTACK_DAMAGE, EARTH_PONY_STRENGTH, race.canUseEarth());
applyAttribute(entity, EntityAttributes.KNOCKBACK_RESISTANCE, EARTH_PONY_STRENGTH, race.canUseEarth());
applyAttribute(entity, EntityAttributes.MOVEMENT_SPEED, PEGASUS_SPEED, race.canFly());
applyAttribute(entity, EntityAttributes.ATTACK_SPEED, PEGASUS_SPEED, race.canFly());
// applyAttribute(entity, PlayerEntity.REACH_DISTANCE, PEGASUS_REACH, race.canFly());
toggleAttribute(entity, EntityAttributes.ATTACK_DAMAGE, EARTH_PONY_STRENGTH, race.canUseEarth());
toggleAttribute(entity, EntityAttributes.KNOCKBACK_RESISTANCE, EARTH_PONY_STRENGTH, race.canUseEarth());
toggleAttribute(entity, EntityAttributes.MOVEMENT_SPEED, PEGASUS_SPEED, race.canFly());
toggleAttribute(entity, EntityAttributes.ATTACK_SPEED, PEGASUS_SPEED, race.canFly());
toggleAttribute(entity, EXTENDED_REACH_DISTANCE, PEGASUS_REACH, race.canFly());
}
private void applyAttribute(PlayerEntity entity, EntityAttribute attribute, EntityAttributeModifier modifier, boolean enable) {
private void toggleAttribute(PlayerEntity entity, EntityAttribute attribute, EntityAttributeModifier modifier, boolean enable) {
EntityAttributeInstance instance = entity.getAttributeInstance(attribute);
if (enable) {

View file

@ -4,7 +4,6 @@ import javax.annotation.Nullable;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.AbilityReceiver;
import com.minelittlepony.unicopia.enchanting.PageOwner;
import com.minelittlepony.unicopia.entity.FlightControl;
@ -16,6 +15,7 @@ import com.minelittlepony.unicopia.magic.HeldMagicEffect;
import com.minelittlepony.unicopia.magic.MagicEffect;
import com.minelittlepony.unicopia.magic.MagicalItem;
import com.minelittlepony.unicopia.magic.spell.SpellRegistry;
import com.minelittlepony.unicopia.network.Channel;
import com.minelittlepony.unicopia.network.EffectSync;
import com.minelittlepony.unicopia.network.MsgPlayerCapabilities;
import com.minelittlepony.unicopia.util.BasicEasingInterpolator;
@ -85,6 +85,8 @@ public class PlayerImpl implements Pony {
player.getDataTracker().startTracking(ENERGY, 0F);
player.getDataTracker().startTracking(EFFECT, new CompoundTag());
player.getDataTracker().startTracking(HELD_EFFECT, new CompoundTag());
player.getAttributes().register(PlayerAttributes.EXTENDED_REACH_DISTANCE);
}
@Override
@ -169,11 +171,7 @@ public class PlayerImpl implements Pony {
dirty = false;
if (!getWorld().isClient()) {
if (full) {
Unicopia.getConnection().broadcast(new MsgPlayerCapabilities(this));
} else {
Unicopia.getConnection().broadcast(new MsgPlayerCapabilities(getSpecies(), getOwner()));
}
Channel.BROADCAST_CAPABILITIES.send(new MsgPlayerCapabilities(full, this));
}
}
@ -199,6 +197,11 @@ public class PlayerImpl implements Pony {
return gravity;
}
@Override
public float getExtendedReach() {
return (float)entity.getAttributeInstance(PlayerAttributes.EXTENDED_REACH_DISTANCE).getValue();
}
@Override
public FlightControl getFlight() {
return gravity;

View file

@ -70,6 +70,12 @@ public interface Pony extends Caster<PlayerEntity>, RaceContainer<PlayerEntity>,
*/
void setExertion(float exertion);
/**
*
* @return
*/
float getExtendedReach();
/**
* Adds player tiredness.
*/

View file

@ -55,7 +55,7 @@ public class ShieldSpell extends AbstractSpell.RangedAreaSpell implements Attach
particlEffect
.ifMissing(source, () -> {
source.addParticle(UParticles.SPHERE, source.getOriginVector(), Vec3d.ZERO);
return null; // TODO: Attachables
return null; // XXX: Attachables
}) // 1, getTint(), 10
.ifPresent(p -> p.setAttribute(0, radius));
}

View file

@ -13,6 +13,8 @@ import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.entity.player.PlayerImpl;
import com.mojang.datafixers.util.Either;
import net.minecraft.entity.EntityDimensions;
import net.minecraft.entity.EntityPose;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
@ -58,7 +60,17 @@ public abstract class MixinPlayerEntity extends LivingEntity implements PonyCont
@Inject(method = "setGameMode(Lnet/minecraft/world/GameMode;)V",
at = @At("RETURN"))
public void setGameMode(GameMode mode, CallbackInfo info) {
private void onSetGameMode(GameMode mode, CallbackInfo info) {
get().setSpecies(get().getSpecies());
}
@Inject(method = "getActiveEyeHeight(Lnet/minecraft/entity/EntityPose;Lnet/minecraft/entity/EntityDimensions;)F",
at = @At("RETURN"),
cancellable = true)
private void onGetActiveEyeHeight(EntityPose pose, EntityDimensions dimensions, CallbackInfoReturnable<Float> info) {
float h = get().getGravity().getEyeHeight();
if (h != 0) {
info.setReturnValue(h);
}
}
}

View file

@ -0,0 +1,86 @@
package com.minelittlepony.unicopia.network;
import java.util.function.Function;
import io.netty.buffer.Unpooled;
import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
import net.fabricmc.fabric.api.network.PacketContext;
import net.fabricmc.fabric.api.network.ServerSidePacketRegistry;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
public interface Channel {
SPacketType<MsgPlayerAbility> PLAYER_ABILITY = clientToServer(new Identifier("unicopia", "player_ability"), MsgPlayerAbility::new);
SPacketType<MsgRequestCapabilities> REQUEST_CAPABILITIES = clientToServer(new Identifier("unicopia", "request_capabilities"), MsgRequestCapabilities::new);
CPacketType<MsgPlayerCapabilities> PLAYER_CAPABILITIES = serverToClient(new Identifier("unicopia", "player_capabilities"), MsgPlayerCapabilities::new);
SPacketType<MsgPlayerCapabilities> BROADCAST_CAPABILITIES = broadcast(PLAYER_CAPABILITIES, MsgPlayerCapabilities::new);
CPacketType<MsgSpawnRainbow> SPAWN_RAINBOW = serverToClient(new Identifier("unicopia", "rainbow_entity"), MsgSpawnRainbow::new);
static void bootstrap() { }
static <T extends Packet> SPacketType<T> clientToServer(Identifier id, Function<PacketByteBuf, T> factory) {
ServerSidePacketRegistry.INSTANCE.register(id, (context, buffer) -> factory.apply(buffer).handle(context));
return () -> id;
}
static <T extends Packet> SPacketType<T> broadcast(CPacketType<T> redirect, Function<PacketByteBuf, T> factory) {
Identifier id = new Identifier(redirect.getId().getNamespace(), "broadcast_" + redirect.getId().getPath());
ServerSidePacketRegistry.INSTANCE.register(id, (context, buffer) -> {
PlayerEntity sender = context.getPlayer();
T p = factory.apply(buffer);
p.handle(context);
sender.world.getPlayers().forEach(player -> {
if (player != null && player != sender) {
redirect.send(player, p);
}
});
});
return () -> id;
}
static <T extends Packet> CPacketType<T> serverToClient(Identifier id, Function<PacketByteBuf, T> factory) {
ClientSidePacketRegistry.INSTANCE.register(id, (context, buffer) -> factory.apply(buffer).handle(context));
return () -> id;
}
interface CPacketType<T extends Packet> {
Identifier getId();
default void send(PlayerEntity recipient, T packet) {
ServerSidePacketRegistry.INSTANCE.sendToPlayer(recipient, getId(), packet.toBuffer());
}
default net.minecraft.network.Packet<?> toPacket(T packet) {
return ServerSidePacketRegistry.INSTANCE.toPacket(getId(), packet.toBuffer());
}
}
interface SPacketType<T extends Packet> {
Identifier getId();
default void send(T packet) {
ClientSidePacketRegistry.INSTANCE.sendToServer(getId(), packet.toBuffer());
}
default net.minecraft.network.Packet<?> toPacket(T packet) {
return ClientSidePacketRegistry.INSTANCE.toPacket(getId(), packet.toBuffer());
}
}
interface Packet {
void handle(PacketContext context);
void toBuffer(PacketByteBuf buffer);
default PacketByteBuf toBuffer() {
PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
toBuffer(buf);
return buf;
}
}
}

View file

@ -1,43 +1,36 @@
package com.minelittlepony.unicopia.network;
import java.util.UUID;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.Expose;
import com.minelittlepony.jumpingcastle.api.Channel;
import com.minelittlepony.jumpingcastle.api.Message;
import com.minelittlepony.unicopia.ability.Ability;
import com.minelittlepony.unicopia.ability.Abilities;
import com.minelittlepony.unicopia.entity.player.Pony;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.MinecraftServer;
import net.fabricmc.fabric.api.network.PacketContext;
import net.minecraft.util.PacketByteBuf;
public class MsgPlayerAbility implements Message, Message.Handler<MsgPlayerAbility> {
public class MsgPlayerAbility implements Channel.Packet {
private static final Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
@Expose
private UUID senderId;
private final String powerIdentifier;
@Expose
private String powerIdentifier;
private final String abilityJson;
@Expose
private String abilityJson;
public MsgPlayerAbility(PlayerEntity player, Ability<?> power, Ability.IData data) {
senderId = player.getUuid();
public MsgPlayerAbility(Ability<?> power, Ability.IData data) {
powerIdentifier = power.getKeyName();
abilityJson = gson.toJson(data, power.getPackageType());
}
private <T extends Ability.IData> void apply(Ability<T> power, Channel channel) {
MinecraftServer server = channel.getServer();
Pony player = Pony.of(server.getPlayerManager().getPlayer(senderId));
public MsgPlayerAbility(PacketByteBuf buffer) {
powerIdentifier = buffer.readString();
abilityJson = buffer.readString();
}
private <T extends Ability.IData> void apply(Ability<T> power, PacketContext context) {
Pony player = Pony.of(context.getPlayer());
if (player == null) {
return;
}
@ -48,7 +41,13 @@ public class MsgPlayerAbility implements Message, Message.Handler<MsgPlayerAbili
}
@Override
public void onPayload(MsgPlayerAbility message, Channel channel) {
Abilities.getInstance().getPowerFromName(powerIdentifier).ifPresent(power -> apply(power, channel));
public void toBuffer(PacketByteBuf buffer) {
buffer.writeString(powerIdentifier);
buffer.writeString(abilityJson);
}
@Override
public void handle(PacketContext context) {
Abilities.getInstance().getPowerFromName(powerIdentifier).ifPresent(power -> apply(power, context));
}
}

View file

@ -1,77 +1,60 @@
package com.minelittlepony.unicopia.network;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.UUID;
import com.google.gson.annotations.Expose;
import com.minelittlepony.jumpingcastle.api.Channel;
import com.minelittlepony.jumpingcastle.api.Message;
import java.io.InputStream;
import java.io.OutputStream;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.entity.player.Pony;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import net.fabricmc.fabric.api.network.PacketContext;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.PacketByteBuf;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
public class MsgPlayerCapabilities implements Message, Message.Handler<MsgPlayerCapabilities> {
@Expose
Race newRace;
public class MsgPlayerCapabilities implements Channel.Packet {
@Expose
UUID senderId;
private final Race newRace;
@Expose
byte[] compoundTag;
private final CompoundTag compoundTag;
public MsgPlayerCapabilities(PacketByteBuf buffer) {
newRace = Race.values()[buffer.readInt()];
try (InputStream in = new ByteBufInputStream(buffer)) {
compoundTag = NbtIo.readCompressed(in);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public MsgPlayerCapabilities(Race race, PlayerEntity player) {
newRace = race;
senderId = player.getUuid();
compoundTag = new byte[0];
compoundTag = new CompoundTag();
}
public MsgPlayerCapabilities(Pony player) {
public MsgPlayerCapabilities(boolean full, Pony player) {
newRace = player.getSpecies();
senderId = player.getOwner().getUuid();
compoundTag = full ? player.toNBT() : new CompoundTag();
}
try (ByteArrayOutputStream bytes = new ByteArrayOutputStream()) {
CompoundTag nbt = player.toNBT();
NbtIo.write(nbt, new DataOutputStream(bytes));
compoundTag = bytes.toByteArray();
@Override
public void toBuffer(PacketByteBuf buffer) {
buffer.writeInt(newRace.ordinal());
try (OutputStream out = new ByteBufOutputStream(buffer)) {
NbtIo.writeCompressed(compoundTag, out);
} catch (IOException e) {
}
}
@Override
public void onPayload(MsgPlayerCapabilities message, Channel channel) {
MinecraftServer server = channel.getServer();
PlayerEntity self = server.getPlayerManager().getPlayer(senderId);
if (self == null) {
Unicopia.LOGGER.warn("[Unicopia] [CLIENT] [MsgPlayerCapabilities] Player with id %s was not found!\n", senderId.toString());
public void handle(PacketContext context) {
Pony player = Pony.of(context.getPlayer());
if (compoundTag.isEmpty()) {
player.setSpecies(newRace);
} else {
Pony player = Pony.of(self);
if (compoundTag.length > 0) {
try (ByteArrayInputStream input = new ByteArrayInputStream(compoundTag)) {
CompoundTag nbt = NbtIo.read(new DataInputStream(input));
player.fromNBT(nbt);
} catch (IOException e) {
}
} else {
player.setSpecies(newRace);
}
player.fromNBT(compoundTag);
}
}
}

View file

@ -1,40 +1,38 @@
package com.minelittlepony.unicopia.network;
import java.util.UUID;
import com.google.gson.annotations.Expose;
import com.minelittlepony.jumpingcastle.api.Channel;
import com.minelittlepony.jumpingcastle.api.Message;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.entity.player.Pony;
import net.fabricmc.fabric.api.network.PacketContext;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.PacketByteBuf;
public class MsgRequestCapabilities implements Message, Message.Handler<MsgRequestCapabilities> {
@Expose
public UUID senderId;
public class MsgRequestCapabilities implements Channel.Packet {
@Expose
public Race race;
private final Race race;
public MsgRequestCapabilities(PacketByteBuf buffer) {
race = Race.values()[buffer.readInt()];
}
public MsgRequestCapabilities(PlayerEntity player, Race preferredRace) {
senderId = player.getGameProfile().getId();
race = preferredRace;
}
@Override
public void onPayload(MsgRequestCapabilities message, Channel channel) {
MinecraftServer server = channel.getServer();
public void toBuffer(PacketByteBuf buffer) {
buffer.writeInt(race.ordinal());
}
Unicopia.LOGGER.warn("[Unicopia] [SERVER] [MsgRequestCapabilities] Sending capabilities to player %s\n", senderId.toString());
Pony player = Pony.of(server.getPlayerManager().getPlayer(senderId));
@Override
public void handle(PacketContext context) {
Pony player = Pony.of(context.getPlayer());
if (player.getSpecies().isDefault()) {
player.setSpecies(message.race);
player.setSpecies(race);
}
channel.respond(new MsgPlayerCapabilities(player), senderId);
Channel.PLAYER_CAPABILITIES.send(context.getPlayer(), new MsgPlayerCapabilities(true, player));
}
}

View file

@ -0,0 +1,52 @@
package com.minelittlepony.unicopia.network;
import com.minelittlepony.unicopia.entity.RainbowEntity;
import com.minelittlepony.unicopia.entity.UEntities;
import net.fabricmc.fabric.api.network.PacketContext;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.Entity;
import net.minecraft.util.PacketByteBuf;
public class MsgSpawnRainbow implements Channel.Packet {
private final int id;
private final double x;
private final double y;
private final double z;
public MsgSpawnRainbow(Entity entity) {
id = entity.getEntityId();
x = entity.getX();
y = entity.getY();
z = entity.getZ();
}
public MsgSpawnRainbow(PacketByteBuf buffer) {
id = buffer.readVarInt();
x = buffer.readDouble();
y = buffer.readDouble();
z = buffer.readDouble();
}
@Override
public void toBuffer(PacketByteBuf buffer) {
buffer.writeVarInt(id);
buffer.writeDouble(x);
buffer.writeDouble(y);
buffer.writeDouble(z);
}
@Override
public void handle(PacketContext context) {
MinecraftClient client = MinecraftClient.getInstance();
RainbowEntity entity = UEntities.RAINBOW.create(client.world);
entity.setPos(x, y, z);
entity.updateTrackedPosition(x, y, z);
entity.yaw = 0;
entity.pitch = 0;
entity.setEntityId(id);
client.world.addEntity(id, entity);
}
}