Add donor perks and tweak generalise rendering of healing totem

This commit is contained in:
Electroblob77
2020-07-19 15:56:07 +01:00
parent a831c2ada3
commit 3343385a7c
28 changed files with 490 additions and 3 deletions
@@ -1,5 +1,6 @@
package electroblob.wizardry;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.item.ItemArtefact;
import electroblob.wizardry.packet.PacketSyncSettings;
import electroblob.wizardry.packet.WizardryPacketHandler;
@@ -25,6 +26,7 @@ import org.apache.commons.lang3.tuple.Pair;
import java.io.File;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Singleton class which deals with everything related to wizardry's config file. To access individual settings, use
@@ -326,6 +328,14 @@ public final class Settings {
/** <b>[Synchronised]</b> List of registry names of items that can be placed in a bookshelf. */
public Pair<ResourceLocation, Short>[] bookItems = parseItemMetaStrings();
/**
* <b>[Synchronised*]</b> The element to render for donation perks, or null if it should not be rendered.
* <p></p>
* * Unlike the other options, <b>this is synchronised client -> server</b>, then the data for all players is sent
* back to every client when they join the game, or whenever it changes.
*/
public Element donationPerkElement = Element.MAGIC;
// Client-only settings. These settings only affect client-side code and hence are not synced. Each client obeys
// its own values for these, and changing them on a dedicated server will have no effect.
@@ -1084,6 +1094,13 @@ public final class Settings {
spellcastingAnimations = property.getBoolean();
propOrder.add(property.getName());
List<String> elementNames = Arrays.stream(Element.values()).map(Element::getName).collect(Collectors.toList());
elementNames.add("");
property = config.get(CLIENT_CATEGORY, "donationPerkElement", Element.MAGIC.getName(), "The element colour to use for the flying companion orb rendered on donor players, leave empty to disable the effect. If you're someone who was kind enough to donate, this setting will change what all players see (if not, it won't do anything).", elementNames.toArray(new String[0]));
property.setLanguageKey("config." + Wizardry.MODID + ".donation_perk_element");
donationPerkElement = Element.fromName(property.getString(), null); // Fallback to null for no element
propOrder.add(property.getName());
config.setCategoryPropertyOrder(CLIENT_CATEGORY, propOrder);
}
@@ -10,6 +10,7 @@ import electroblob.wizardry.data.WizardData;
import electroblob.wizardry.integration.antiqueatlas.WizardryAntiqueAtlasIntegration;
import electroblob.wizardry.integration.baubles.WizardryBaublesIntegration;
import electroblob.wizardry.inventory.ContainerBookshelf;
import electroblob.wizardry.misc.DonationPerksHandler;
import electroblob.wizardry.misc.Forfeit;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.registry.*;
@@ -197,7 +198,12 @@ public class Wizardry {
public void onConfigChanged(net.minecraftforge.fml.client.event.ConfigChangedEvent.OnConfigChangedEvent event){
if(event.getModID().equals(Wizardry.MODID)){
settings.saveConfigChanges();
// All of the synchronised settings require a world restart anyway so this doesn't need syncing.
// All of the synchronised settings require a world restart anyway so don't need syncing, except for the
// donor perks which have special behaviour (since we have to update everyone when a donor logs in anyway,
// we might as well reuse the packet and let them change the setting mid-game)
if(event.isWorldRunning() && DonationPerksHandler.isDonor(proxy.getThePlayer())){
DonationPerksHandler.sendToServer(proxy.getThePlayer());
}
}
}
@@ -79,11 +79,13 @@ public final class WizardryEventHandler {
public static void onPlayerLoggedInEvent(PlayerLoggedInEvent event){
// When a player logs in, they are sent the glyph data, server settings and spell properties.
if(event.player instanceof EntityPlayerMP){
// TODO: Move these to handler classes where possible
SpellGlyphData.get(event.player.world).sync((EntityPlayerMP)event.player);
SpellEmitterData.get(event.player.world).sync((EntityPlayerMP)event.player);
Wizardry.settings.sync((EntityPlayerMP)event.player);
syncAdvancements((EntityPlayerMP)event.player, false);
}
Spell.syncProperties(event.player);
}
@@ -0,0 +1,190 @@
package electroblob.wizardry.client.renderer.effect;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.BlockReceptacle;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.misc.DonationPerksHandler;
import electroblob.wizardry.util.GeometryUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.client.event.RenderPlayerEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.lwjgl.opengl.GL11;
@EventBusSubscriber
public class RenderDonationPerks {
private static final ResourceLocation FLARE_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/totem/flare.png");
private static final ResourceLocation[] CUBE_TEXTURES = new ResourceLocation[14];
private static final float BOBBING_PERIOD = 25;
private static final float ROTATION_PERIOD = 60;
private static final double ROTATION_RADIUS = 0.7;
private static final double HEIGHT_FRACTION = 0.8;
private static final double BOBBING_DISTANCE = 0.2;
private static final double FOLLOW_DISTANCE = 1.4;
static {
for(int i = 0; i< CUBE_TEXTURES.length; i++) CUBE_TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/totem/cube_" + i + ".png");
}
@SubscribeEvent
public static void onRenderPlayerEvent(RenderPlayerEvent.Post event){
EntityPlayer player = event.getEntityPlayer();
float partialTicks = event.getPartialRenderTick();
Element element = DonationPerksHandler.getElement(player);
if(element != null){
int c1 = BlockReceptacle.PARTICLE_COLOURS.get(element)[0];
int r1 = (c1 & 0xFF0000) >> 16;
int g1 = (c1 & 0xFF00) >> 8;
int b1 = (c1 & 0xFF);
int c2 = BlockReceptacle.PARTICLE_COLOURS.get(element)[1];
int r2 = (c2 & 0xFF0000) >> 16;
int g2 = (c2 & 0xFF00) >> 8;
int b2 = (c2 & 0xFF);
Tessellator tessellator = Tessellator.getInstance();
GlStateManager.pushMatrix();
GlStateManager.enableBlend();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240);
GlStateManager.depthMask(false);
GlStateManager.disableCull();
double x1 = player.chasingPosX - player.posX;
double y1 = player.chasingPosY - player.posY;
double z1 = player.chasingPosZ - player.posZ;
double x2 = player.prevChasingPosX - player.prevPosX;
double y2 = player.prevChasingPosY - player.prevPosY;
double z2 = player.prevChasingPosZ - player.prevPosZ;
double dx = x2 + (x1 - x2) * partialTicks;
double dy = y2 + (y1 - y2) * partialTicks;
double dz = z2 + (z1 - z2) * partialTicks;
float t = player.ticksExisted + partialTicks;
float hMoveFraction = MathHelper.clamp((float)(dx * dx + dz * dz) / 0.3f, 0, 1);
float vMoveFraction = MathHelper.clamp((float)(dy * dy) / 0.3f, 0, 1);
double x = event.getX() + ROTATION_RADIUS * MathHelper.sin(t / ROTATION_PERIOD) * (1 - hMoveFraction) + FOLLOW_DISTANCE * dx;
double y = event.getY() + HEIGHT_FRACTION * player.height + BOBBING_DISTANCE * MathHelper.sin(t / BOBBING_PERIOD) * (1 - vMoveFraction) + FOLLOW_DISTANCE * dy;
double z = event.getZ() + ROTATION_RADIUS * MathHelper.cos(t / ROTATION_PERIOD) * (1 - hMoveFraction) + FOLLOW_DISTANCE * dz;
GlStateManager.translate(x, y, z);
GlStateManager.scale(0.4, 0.4, 0.4);
// Makes the colour add to the colour of the texture pixels, rather than the default multiplying
GlStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_ADD);
GlStateManager.color(r1/255f, g1/255f, b1/255f);
drawFlare(tessellator);
// Reverses the colour addition change
GlStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
GlStateManager.color(r2/255f, g2/255f, b2/255f);
drawCube(player, tessellator, partialTicks);
GlStateManager.color(1, 1, 1,1);
GlStateManager.disableBlend();
GlStateManager.enableLighting();
GlStateManager.depthMask(true);
GlStateManager.enableCull();
GlStateManager.popMatrix();
}
}
private static void drawFlare(Tessellator tessellator){
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.pushMatrix();
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
Minecraft.getMinecraft().renderEngine.bindTexture(FLARE_TEXTURE);
// This counteracts the reverse rotation behaviour when in front f5 view.
// Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example.
float yaw = Minecraft.getMinecraft().gameSettings.thirdPersonView == 2
? Minecraft.getMinecraft().getRenderManager().playerViewX
: -Minecraft.getMinecraft().getRenderManager().playerViewX;
GlStateManager.rotate(180.0F - Minecraft.getMinecraft().getRenderManager().playerViewY, 0.0F, 1.0F, 0.0F);
GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
float r = 0.5f;
buffer.pos(-r, r, 0).tex(0, 0).endVertex();
buffer.pos( r, r, 0).tex(1, 0).endVertex();
buffer.pos( r, -r, 0).tex(1, 1).endVertex();
buffer.pos(-r, -r, 0).tex(0, 1).endVertex();
tessellator.draw();
GlStateManager.popMatrix();
}
private static void drawCube(EntityPlayer player, Tessellator tessellator, float partialTicks){
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.pushMatrix();
float age = player.ticksExisted + partialTicks;
float rotationSpeed = 2;
GlStateManager.rotate(age * rotationSpeed/2, 0.0F, 1.0F, 0.0F);
GlStateManager.rotate(60.0F, 0.7071F, 0.0F, 0.7071F);
GlStateManager.rotate(age * rotationSpeed, 0.0F, 1.0F, 0.0F);
GlStateManager.scale(0.5, 0.5, 0.5);
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
Minecraft.getMinecraft().renderEngine.bindTexture(CUBE_TEXTURES[player.ticksExisted % CUBE_TEXTURES.length]);
Vec3d[] vertices = GeometryUtils.getVertices(new AxisAlignedBB(-0.5, -0.5, -0.5, 0.5, 0.5, 0.5));
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
// Outside
drawFace(buffer, vertices[0], vertices[1], vertices[3], vertices[2], 0.5f, 0, 0.75f, 0.5f); // Bottom
drawFace(buffer, vertices[6], vertices[7], vertices[2], vertices[3], 0.75f, 0.5f, 1, 1); // South
drawFace(buffer, vertices[5], vertices[6], vertices[1], vertices[2], 0, 0.5f, 0.25f, 1); // East
drawFace(buffer, vertices[4], vertices[5], vertices[0], vertices[1], 0.25f, 0.5f, 0.5f, 1); // North
drawFace(buffer, vertices[7], vertices[4], vertices[3], vertices[0], 0.5f, 0.5f, 0.75f, 1); // West
drawFace(buffer, vertices[5], vertices[4], vertices[6], vertices[7], 0.25f, 0, 0.5f, 0.5f); // Top
tessellator.draw();
GlStateManager.popMatrix();
}
private static void drawFace(BufferBuilder buffer, Vec3d topLeft, Vec3d topRight, Vec3d bottomLeft, Vec3d bottomRight, float u1, float v1, float u2, float v2){
buffer.pos(topLeft.x, topLeft.y, topLeft.z).tex(u1, v1).endVertex();
buffer.pos(topRight.x, topRight.y, topRight.z).tex(u2, v1).endVertex();
buffer.pos(bottomRight.x, bottomRight.y, bottomRight.z).tex(u2, v2).endVertex();
buffer.pos(bottomLeft.x, bottomLeft.y, bottomLeft.z).tex(u1, v2).endVertex();
}
}
@@ -20,11 +20,11 @@ import javax.annotation.Nullable;
public class RenderRadiantTotem extends Render<EntityRadiantTotem> {
private static final ResourceLocation FLARE_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/radiant_totem/flare.png");
private static final ResourceLocation FLARE_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/totem/flare.png");
private static final ResourceLocation[] CUBE_TEXTURES = new ResourceLocation[14];
static {
for(int i = 0; i< CUBE_TEXTURES.length; i++) CUBE_TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/radiant_totem/cube_" + i + ".png");
for(int i = 0; i< CUBE_TEXTURES.length; i++) CUBE_TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/totem/cube_" + i + ".png");
}
public RenderRadiantTotem(RenderManager manager){
@@ -72,6 +72,9 @@ public class RenderRadiantTotem extends Render<EntityRadiantTotem> {
GlStateManager.pushMatrix();
// Makes the colour add to the colour of the texture pixels, rather than the default multiplying
GlStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_ADD);
GlStateManager.color(1, 0.957f, 0.608f);
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
bindTexture(FLARE_TEXTURE);
@@ -95,6 +98,9 @@ public class RenderRadiantTotem extends Render<EntityRadiantTotem> {
tessellator.draw();
// Reverses the colour addition change
GlStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
GlStateManager.color(1, 1, 1, 1);
GlStateManager.popMatrix();
}
@@ -8,6 +8,8 @@ import net.minecraft.util.text.Style;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.util.text.TextFormatting;
import javax.annotation.Nullable;
public enum Element implements IStringSerializable {
/** The 'default' element, with {@link electroblob.wizardry.registry.Spells#magic_missile magic missile} being its
@@ -49,6 +51,18 @@ public enum Element implements IStringSerializable {
throw new IllegalArgumentException("No such element with unlocalised name: " + name);
}
/** Same as {@link Element#fromName(String)}, but returns the given fallback instead of throwing an exception if no
* element matches the given string. */
@Nullable
public static Element fromName(String name, @Nullable Element fallback){
for(Element element : values()){
if(element.unlocalisedName.equals(name)) return element;
}
return fallback;
}
/** Returns the translated display name of this element, without formatting. */
public String getDisplayName(){
return Wizardry.proxy.translate("element." + getName());
@@ -0,0 +1,102 @@
package electroblob.wizardry.misc;
import com.google.common.collect.ImmutableMap;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.packet.PacketRequestDonationPerks;
import electroblob.wizardry.packet.PacketSyncDonationPerks;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.util.Box;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
/** Class responsible for storing and handling the donation perk settings for all eligible connected players */
// We probably could have used WizardData for this, but since this is config-based and only for rendering it makes more
// sense to handle it separately, especially given that the vast majority of players aren't donors
@Mod.EventBusSubscriber
public class DonationPerksHandler {
// Thanks to everyone who has donated their time and/or money! <3 (These are your UUIDs - don't worry, they're not
// sensitive info, they're just player identifiers that won't ever change, even if you change your username)
// Convert between usernames and UUIDs here https://mcuuid.net/
// Normally I'd use an EnumMap for this, but somehow making the UUIDs into an enum doesn't seem right
private static final Map<UUID, Box<Element>> DONOR_UUID_MAP = ImmutableMap.of(
UUID.fromString("4b29263e-007b-48ef-b3e6-ce86cca989e9"), new Box<>(Element.MAGIC), // Me!
UUID.fromString("2b583703-1407-4ec6-93d2-d9c03b0c08fc"), new Box<>(Element.MAGIC),
UUID.fromString("f6c7001f-4ce8-43f7-b4a1-a9d9d5fadb93"), new Box<>(Element.MAGIC),
UUID.fromString("01535a73-ff8d-4d6c-851e-c71f89e936aa"), new Box<>(Element.MAGIC)
);
/** Returns true if the given player's UUID appears in the list of donor UUIDs, false otherwise (thread-safe). */
public static boolean isDonor(EntityPlayer player){
return DONOR_UUID_MAP.containsKey(player.getUniqueID());
}
/** Sets each donor's perk element in order according to the given list. The given list <i>will</i> be modified. */
public static void setElements(List<Element> elements){
if(elements.size() < DONOR_UUID_MAP.size()){
Wizardry.logger.warn("Received fewer perk settings than the number of donors, cannot sync!");
return;
}
DONOR_UUID_MAP.forEach((k, v) -> v.set(elements.remove(0)));
if(!elements.isEmpty()) Wizardry.logger.warn("Received more perk settings than the number of donors, something must have gone wrong!");
}
/** Sets the donor perk to the given element for the given player and sends a packet to all clients if it changed. */
public static void setElement(EntityPlayerMP player, Element element){
Box<Element> box = DONOR_UUID_MAP.get(player.getUniqueID());
if(box != null && box.get() != element){
box.set(element);
syncAll();
}
}
/** Returns the given player's donor perk element, or null if they are not a donor (may also be null for donors if
* they have disabled the perk). */
public static Element getElement(EntityPlayer player){
Box<Element> box = DONOR_UUID_MAP.get(player.getUniqueID());
return box == null ? null : box.get();
}
/** Sends the current donor perk information to all connected players (server-side only). */
private static void syncAll(){
Wizardry.logger.info("Sending global donation perk settings to all players");
PacketSyncDonationPerks.Message packet = new PacketSyncDonationPerks.Message(DONOR_UUID_MAP.values().stream().map(Box::get).collect(Collectors.toList()));
WizardryPacketHandler.net.sendToAll(packet);
}
/** Sends the current donor perk information to the given player (server-side only). */
private static void syncWith(EntityPlayerMP player){
Wizardry.logger.info("Sending global donation perk settings to {}", player.getName());
PacketSyncDonationPerks.Message packet = new PacketSyncDonationPerks.Message(DONOR_UUID_MAP.values().stream().map(Box::get).collect(Collectors.toList()));
WizardryPacketHandler.net.sendTo(packet, player);
}
/** Sends the client player's donor perk setting to the server (client-side only) */
public static void sendToServer(EntityPlayer player){
Wizardry.logger.info("Sending donation perk settings for {} to server", player.getName());
IMessage message = new PacketRequestDonationPerks.Message(Wizardry.settings.donationPerkElement);
WizardryPacketHandler.net.sendToServer(message);
}
@SubscribeEvent
public static void onPlayerLoggedInEvent(PlayerLoggedInEvent event){
if(event.player instanceof EntityPlayerMP){
// Send donation perks to anyone who logs in
syncWith((EntityPlayerMP)event.player);
}else{
// Send donation perks setting to the server if the player is a donor
if(isDonor(event.player)) sendToServer(event.player);
}
}
}
@@ -0,0 +1,58 @@
package electroblob.wizardry.packet;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.misc.DonationPerksHandler;
import io.netty.buffer.ByteBuf;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
/** <b>[Client -> Server]</b> This packet is sent when a player logs in and whenever the setting changes to set their
* donation perk element. */
public class PacketRequestDonationPerks implements IMessageHandler<PacketRequestDonationPerks.Message, IMessage> {
@Override
public IMessage onMessage(Message message, MessageContext ctx){
// Just to make sure that the side is correct
if(ctx.side.isServer()){
final EntityPlayerMP player = ctx.getServerHandler().player;
// The UUID key set itself is immutable so we can safely access it from the networking thread
if(DonationPerksHandler.isDonor(player)){
player.getServerWorld().addScheduledTask(() -> DonationPerksHandler.setElement(player, message.element));
}else{
Wizardry.logger.warn("Received a donation perk packet from a player that isn't a donor!");
}
}
return null;
}
public static class Message implements IMessage {
private Element element;
// This constructor is required otherwise you'll get errors (used somewhere in fml through reflection)
public Message(){}
public Message(Element element){
this.element = element;
}
@Override
public void fromBytes(ByteBuf buf){
// The order is important
element = Element.values()[buf.readShort()];
}
@Override
public void toBytes(ByteBuf buf){
buf.writeShort(element.ordinal());
}
}
}
@@ -0,0 +1,55 @@
package electroblob.wizardry.packet;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.misc.DonationPerksHandler;
import io.netty.buffer.ByteBuf;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
import java.util.ArrayList;
import java.util.List;
/**
* <b>[Server -> Client]</b> This packet is sent to update the donation perks map for each player that logs in, and for
* all players whenever the map changes.
*/
public class PacketSyncDonationPerks implements IMessageHandler<PacketSyncDonationPerks.Message, IMessage> {
@Override
public IMessage onMessage(Message message, MessageContext ctx){
// Just to make sure that the side is correct
if(ctx.side.isClient()){
// Using a fully qualified name is a good course of action here; we don't really want to clutter the proxy
// methods any more than necessary.
net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(() -> DonationPerksHandler.setElements(message.elements));
}
return null;
}
public static class Message implements IMessage {
public List<Element> elements;
// This constructor is required otherwise you'll get errors (used somewhere in fml through reflection)
public Message(){}
public Message(List<Element> elements){
this.elements = elements;
}
@Override
public void fromBytes(ByteBuf buf){
// The order is important
this.elements = new ArrayList<>();
while(buf.isReadable()) elements.add(Element.values()[buf.readShort()]);
}
@Override
public void toBytes(ByteBuf buf){
for(Element element : elements) buf.writeShort(element.ordinal());
}
}
}
@@ -33,6 +33,8 @@ public class WizardryPacketHandler {
registerMessage(PacketConquerShrine.class, PacketConquerShrine.Message.class);
registerMessage(PacketLectern.class, PacketLectern.Message.class);
registerMessage(PacketSpellQuickAccess.class, PacketSpellQuickAccess.Message.class);
registerMessage(PacketRequestDonationPerks.class, PacketRequestDonationPerks.Message.class);
registerMessage(PacketSyncDonationPerks.class, PacketSyncDonationPerks.Message.class);
}
private static int nextPacketId = 0;
@@ -0,0 +1,33 @@
package electroblob.wizardry.util;
/**
* A very simple container for a single object of a given type {@code T} (referred to as the <i>contents</i>). Allows
* fixed-size collections to be made using their immutable (or unmodifiable) counterparts. This is particularly useful
* for maps with a fixed set of keys but whose values may be changed.
*/
public class Box<T> {
private T contents;
public Box(T contents){
this.contents = contents;
}
/** Returns the contents of this box. */
public T get(){
return contents;
}
/** Sets the contents of this box to the given object. */
public void set(T contents){
this.contents = contents;
}
// Probably misleading since methods like this generally imply immutability, which is exactly what we're not doing
// /** Creates a new box with the given contents. This is just a static factory method that calls the constructor. */
// public static <T> Box<T> of(T contents){
// return new Box<>(contents);
// }
}