Overhauled Server-Side Packet Handling

Threw out the complicated architecture that I'd made for synchronizing
server and client data perfectly. Instead, we now send just enough data
to the client and the resulting code is simpler. Some of the client-side
code is also done so all packet handling should be finished soon.
This commit is contained in:
SenseiKiwi 2013-09-03 15:33:09 -04:00
parent 307d2258d1
commit 3568d223ff
17 changed files with 377 additions and 459 deletions

View file

@ -11,7 +11,6 @@ import net.minecraft.network.packet.Packet1Login;
import net.minecraft.network.packet.Packet250CustomPayload;
import net.minecraft.server.MinecraftServer;
import StevenDimDoors.mod_pocketDim.core.PocketManager;
import StevenDimDoors.mod_pocketDim.messages.IDataMessage;
import cpw.mods.fml.common.network.IConnectionHandler;
import cpw.mods.fml.common.network.Player;
@ -41,12 +40,11 @@ public class ConnectionHandler implements IConnectionHandler
//Send information about all the registered dimensions and links to the client
try
{
IDataMessage message = PocketManager.getState();
Packet250CustomPayload packet = new Packet250CustomPayload();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream writer = new DataOutputStream(buffer);
writer.writeByte(PacketConstants.CLIENT_JOIN_PACKET_ID);
message.writeToStream(writer);
PocketManager.writePacket(writer);
writer.close();
packet.channel = PacketConstants.CHANNEL_NAME;
packet.data = buffer.toByteArray();

View file

@ -7,8 +7,9 @@ import java.io.IOException;
import net.minecraft.network.INetworkManager;
import net.minecraft.network.packet.Packet250CustomPayload;
import StevenDimDoors.mod_pocketDim.core.PocketManager;
import StevenDimDoors.mod_pocketDim.messages.IDataMessage;
import StevenDimDoors.mod_pocketDim.messages.IUpdateWatcher;
import StevenDimDoors.mod_pocketDim.util.Point4D;
import StevenDimDoors.mod_pocketDim.watcher.ClientDimData;
import StevenDimDoors.mod_pocketDim.watcher.IUpdateWatcher;
import cpw.mods.fml.common.network.IPacketHandler;
import cpw.mods.fml.common.network.PacketDispatcher;
import cpw.mods.fml.common.network.Player;
@ -22,54 +23,39 @@ public class ServerPacketHandler implements IPacketHandler
}
@Override
public void onPacketData(INetworkManager manager, Packet250CustomPayload packet, Player player)
{
}
public void onPacketData(INetworkManager manager, Packet250CustomPayload packet, Player player) { }
private class DimWatcher implements IUpdateWatcher
private static class DimWatcher implements IUpdateWatcher<ClientDimData>
{
@Override
public void onCreated(IDataMessage message)
public void onCreated(ClientDimData message)
{
sendMessageToAllPlayers(PacketConstants.CREATE_DIM_PACKET_ID, message);
sendDimPacket(PacketConstants.CREATE_DIM_PACKET_ID, message);
}
@Override
public void onUpdated(IDataMessage message)
public void onDeleted(ClientDimData message)
{
sendMessageToAllPlayers(PacketConstants.UPDATE_DIM_PACKET_ID, message);
}
@Override
public void onDeleted(IDataMessage message)
{
sendMessageToAllPlayers(PacketConstants.DELETE_DIM_PACKET_ID, message);
sendDimPacket(PacketConstants.DELETE_DIM_PACKET_ID, message);
}
}
private class LinkWatcher implements IUpdateWatcher
private static class LinkWatcher implements IUpdateWatcher<Point4D>
{
@Override
public void onCreated(IDataMessage message)
public void onCreated(Point4D message)
{
sendMessageToAllPlayers(PacketConstants.CREATE_LINK_PACKET_ID, message);
sendLinkPacket(PacketConstants.CREATE_LINK_PACKET_ID, message);
}
@Override
public void onUpdated(IDataMessage message)
public void onDeleted(Point4D message)
{
sendMessageToAllPlayers(PacketConstants.UPDATE_LINK_PACKET_ID, message);
}
@Override
public void onDeleted(IDataMessage message)
{
sendMessageToAllPlayers(PacketConstants.DELETE_LINK_PACKET_ID, message);
sendLinkPacket(PacketConstants.DELETE_LINK_PACKET_ID, message);
}
}
private static void sendMessageToAllPlayers(byte id, IDataMessage message)
private static void sendDimPacket(byte id, ClientDimData data)
{
try
{
@ -77,7 +63,29 @@ public class ServerPacketHandler implements IPacketHandler
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream writer = new DataOutputStream(buffer);
writer.writeByte(id);
message.writeToStream(writer);
data.write(writer);
writer.close();
packet.channel = PacketConstants.CHANNEL_NAME;
packet.data = buffer.toByteArray();
packet.length = packet.data.length;
PacketDispatcher.sendPacketToAllPlayers(packet);
}
catch (IOException e)
{
//This shouldn't happen...
e.printStackTrace();
}
}
private static void sendLinkPacket(byte id, Point4D data)
{
try
{
Packet250CustomPayload packet = new Packet250CustomPayload();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream writer = new DataOutputStream(buffer);
writer.writeByte(id);
Point4D.write(data, writer);
writer.close();
packet.channel = PacketConstants.CHANNEL_NAME;
packet.data = buffer.toByteArray();

View file

@ -1,30 +1,29 @@
package StevenDimDoors.mod_pocketDim.core;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import StevenDimDoors.mod_pocketDim.util.Point4D;
public abstract class DimLink
{
private static final int EXPECTED_CHILDREN = 2;
{
protected Point4D source;
protected DimLink parent;
protected LinkTail tail;
protected ArrayList<DimLink> children;
protected List<DimLink> children;
protected DimLink(Point4D source, DimLink parent)
{
this.parent = parent;
this.source = source;
this.tail = parent.tail;
this.children = new ArrayList<DimLink>(EXPECTED_CHILDREN);
this.children = new LinkedList<DimLink>();
parent.children.add(this);
}
protected DimLink(Point4D source, int linkType)
{
if (linkType < LinkTypes.ENUM_MIN || linkType > LinkTypes.ENUM_MAX)
if (linkType < LinkTypes.ENUM_MIN || linkType > LinkTypes.ENUM_MAX && linkType != LinkTypes.UNKNOWN)
{
throw new IllegalArgumentException("The specified link type is invalid.");
}
@ -32,7 +31,7 @@ public abstract class DimLink
this.parent = null;
this.source = source;
this.tail = new LinkTail(linkType, null);
this.children = new ArrayList<DimLink>(EXPECTED_CHILDREN);
this.children = new LinkedList<DimLink>();
}
public Point4D source()

View file

@ -0,0 +1,6 @@
package StevenDimDoors.mod_pocketDim.core;
public interface IDimRegistrationCallback
{
public NewDimData registerDimension(int dimensionID, int rootID);
}

View file

@ -7,6 +7,8 @@ public class LinkTypes
public static final int ENUM_MIN = 0;
public static final int ENUM_MAX = 8;
public static final int UNKNOWN = -1337;
public static final int NORMAL = 0;
public static final int LIMBO = 1;
public static final int POCKET = 2;

View file

@ -10,10 +10,8 @@ import net.minecraft.world.World;
import StevenDimDoors.mod_pocketDim.DDProperties;
import StevenDimDoors.mod_pocketDim.dungeon.DungeonData;
import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonPack;
import StevenDimDoors.mod_pocketDim.messages.IDataMessage;
import StevenDimDoors.mod_pocketDim.messages.IUpdateWatcher;
import StevenDimDoors.mod_pocketDim.messages.LinkMessageBuilder;
import StevenDimDoors.mod_pocketDim.util.Point4D;
import StevenDimDoors.mod_pocketDim.watcher.IUpdateWatcher;
public abstract class NewDimData
{
@ -106,19 +104,8 @@ public abstract class NewDimData
parent = null;
tail = new LinkTail(linkType, null);
}
public IDataMessage toMessage()
{
return linkMessageBuilder.createMessage(this);
}
public IDataMessage toKey()
{
return linkMessageBuilder.createKey(this);
}
}
private static LinkMessageBuilder linkMessageBuilder = new LinkMessageBuilder();
private static Random random = new Random();
private final int id;
@ -134,13 +121,12 @@ public abstract class NewDimData
private Point4D origin;
private int orientation;
private DungeonData dungeon;
private final IUpdateWatcher dimWatcher;
private final IUpdateWatcher linkWatcher;
private final IUpdateWatcher<Point4D> linkWatcher;
protected NewDimData(int id, NewDimData parent, boolean isPocket, boolean isDungeon,
IUpdateWatcher dimWatcher, IUpdateWatcher linkWatcher)
IUpdateWatcher<Point4D> linkWatcher)
{
//The isPocket flag is redundant. It's meant as an integrity safeguard.
// The isPocket flag is redundant. It's meant as an integrity safeguard.
if (isPocket == (parent != null))
{
throw new NullPointerException("Dimensions can be pocket dimensions if and only if they have a parent dimension.");
@ -161,7 +147,6 @@ public abstract class NewDimData
this.orientation = 0;
this.origin = null;
this.dungeon = null;
this.dimWatcher = dimWatcher;
this.linkWatcher = linkWatcher;
//Register with parent
@ -179,8 +164,32 @@ public abstract class NewDimData
}
}
protected abstract IDataMessage toMessage();
protected abstract IDataMessage toKey();
protected NewDimData(int id, NewDimData root)
{
// This constructor is meant for client-side code only
this.id = id;
this.linkMapping = new TreeMap<Point4D, InnerDimLink>(); //Should be stored in oct tree -- temporary solution
this.linkList = new ArrayList<InnerDimLink>(); //Should be stored in oct tree -- temporary solution
this.children = new ArrayList<NewDimData>();
this.parent = null;
this.packDepth = 0;
this.isDungeon = false;
this.isFilled = false;
this.orientation = 0;
this.origin = null;
this.dungeon = null;
this.linkWatcher = null;
this.depth = 0;
if (root != null)
{
this.root = root;
}
else
{
this.root = this;
}
}
public DimLink findNearestRift(World world, int range, int x, int y, int z)
{
@ -251,7 +260,7 @@ public abstract class NewDimData
link.overwrite(linkType);
}
//Link created!
linkWatcher.onCreated(link.toMessage());
linkWatcher.onCreated(link.source);
return link;
}
@ -278,14 +287,14 @@ public abstract class NewDimData
linkList.add(link);
//Link created!
linkWatcher.onCreated(link.toMessage());
linkWatcher.onCreated(link.source);
}
else
{
if (link.overwrite(parent))
{
//Link created!
linkWatcher.onCreated(link.toMessage());
linkWatcher.onCreated(link.source);
}
}
return link;
@ -302,7 +311,7 @@ public abstract class NewDimData
{
linkList.remove(target);
//Raise deletion event
linkWatcher.onDeleted(target.toKey());
linkWatcher.onDeleted(target.source);
target.clear();
}
return (target != null);
@ -316,7 +325,7 @@ public abstract class NewDimData
{
linkList.remove(target);
//Raise deletion event
linkWatcher.onDeleted(target.toKey());
linkWatcher.onDeleted(target.source);
target.clear();
}
return (target != null);
@ -345,7 +354,7 @@ public abstract class NewDimData
public boolean isPocketDimension()
{
return (parent != null);
return (root != this);
}
public boolean isDungeon()
@ -361,8 +370,6 @@ public abstract class NewDimData
public void setFilled(boolean isFilled)
{
this.isFilled = isFilled;
//Raise the dim update event
dimWatcher.onUpdated(this.toMessage());
}
public int id()
@ -412,7 +419,7 @@ public abstract class NewDimData
public int linkCount()
{
return linkMapping.size();
return linkList.size();
}
public Iterable<NewDimData> children()
@ -420,6 +427,11 @@ public abstract class NewDimData
return children;
}
public Iterable<? extends DimLink> links()
{
return linkList;
}
public void initializeDungeon(int originX, int originY, int originZ, int orientation, DimLink incoming, DungeonData dungeon)
{
if (!isDungeon)
@ -436,8 +448,6 @@ public abstract class NewDimData
this.orientation = orientation;
this.dungeon = dungeon;
this.packDepth = calculatePackDepth(parent, dungeon);
//Raise the dim update event
dimWatcher.onUpdated(this.toMessage());
}
private static int calculatePackDepth(NewDimData parent, DungeonData current)
@ -486,16 +496,12 @@ public abstract class NewDimData
setDestination(incoming, originX, originY, originZ);
this.origin = incoming.destination();
this.orientation = orientation;
//Raise the dim update event
dimWatcher.onUpdated(this.toMessage());
}
public void setDestination(DimLink incoming, int x, int y, int z)
{
InnerDimLink link = (InnerDimLink) incoming;
link.setDestination(x, y, z, this);
//Raise update event
linkWatcher.onUpdated(link.toMessage());
}
public DimLink getRandomLink()

View file

@ -1,9 +1,8 @@
package StevenDimDoors.mod_pocketDim.core;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
@ -13,13 +12,13 @@ import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import net.minecraftforge.common.DimensionManager;
import StevenDimDoors.mod_pocketDim.DDProperties;
import StevenDimDoors.mod_pocketDim.helpers.Compactor;
import StevenDimDoors.mod_pocketDim.helpers.DeleteFolder;
import StevenDimDoors.mod_pocketDim.messages.DimMessageBuilder;
import StevenDimDoors.mod_pocketDim.messages.IDataMessage;
import StevenDimDoors.mod_pocketDim.messages.IUpdateWatcher;
import StevenDimDoors.mod_pocketDim.messages.UpdateWatcherProxy;
import StevenDimDoors.mod_pocketDim.tileentities.TileEntityRift;
import StevenDimDoors.mod_pocketDim.util.Point4D;
import StevenDimDoors.mod_pocketDim.watcher.ClientDimData;
import StevenDimDoors.mod_pocketDim.watcher.IUpdateWatcher;
import StevenDimDoors.mod_pocketDim.watcher.UpdateWatcherProxy;
/**
* This class regulates all the operations involving the storage and manipulation of dimensions. It handles saving dim data, teleporting the player, and
@ -29,41 +28,40 @@ public class PocketManager
{
private static class InnerDimData extends NewDimData
{
//This inner class allows us to instantiate NewDimData indirectly without exposing
//a public constructor from NewDimData. It's meant to stop us from constructing instances
//of NewDimData without using PocketManager's functions. In turn, that enforces that any
//link destinations must be real dimensions controlled by PocketManager.
// This class allows us to instantiate NewDimData indirectly without exposing
// a public constructor from NewDimData. It's meant to stop us from constructing
// instances of NewDimData going through PocketManager. In turn, that enforces
// that any link destinations must be real dimensions controlled by PocketManager.
public InnerDimData(int id, NewDimData parent, boolean isPocket, boolean isDungeon,
IUpdateWatcher dimWatcher, IUpdateWatcher linkWatcher)
public InnerDimData(int id, InnerDimData parent, boolean isPocket, boolean isDungeon,
IUpdateWatcher<Point4D> linkWatcher)
{
super(id, parent, isPocket, isDungeon, dimWatcher, linkWatcher);
super(id, parent, isPocket, isDungeon, linkWatcher);
}
@Override
protected IDataMessage toMessage()
public InnerDimData(int id, InnerDimData root)
{
return dimMessageBuilder.createMessage(this);
// This constructor is meant for client-side code only
super(id, root);
}
@Override
protected IDataMessage toKey()
public InnerDimData(int id)
{
return dimMessageBuilder.createKey(this);
// This constructor is meant for client-side code only
super(id, null);
}
}
private static DimMessageBuilder dimMessageBuilder = new DimMessageBuilder();
private static int OVERWORLD_DIMENSION_ID = 0;
private static volatile boolean isLoading = false;
private static volatile boolean isLoaded = false;
private static volatile boolean isSaving = false;
private static UpdateWatcherProxy linkWatcher = null;
private static UpdateWatcherProxy dimWatcher = null;
private static UpdateWatcherProxy<Point4D> linkWatcher = null;
private static UpdateWatcherProxy<ClientDimData> dimWatcher = null;
//HashMap that maps all the dimension IDs registered with DimDoors to their DD data.
private static HashMap<Integer, NewDimData> dimensionData = new HashMap<Integer, NewDimData>();
private static HashMap<Integer, InnerDimData> dimensionData = null;
public static boolean isLoaded()
{
@ -87,16 +85,17 @@ public class PocketManager
isLoading = true;
//Set up watcher proxies
dimWatcher = new UpdateWatcherProxy();
linkWatcher = new UpdateWatcherProxy();
loadInternal();
//Set up fields
dimensionData = new HashMap<Integer, InnerDimData>();
dimWatcher = new UpdateWatcherProxy<ClientDimData>();
linkWatcher = new UpdateWatcherProxy<Point4D>();
//Register Limbo
DDProperties properties = DDProperties.instance();
registerDimension(properties.LimboDimensionID, null, false, false);
loadInternal();
//Register pocket dimensions
registerPockets(properties);
@ -134,7 +133,7 @@ public class PocketManager
DeleteFolder.deleteFolder(save);
}
//Raise the dim deleted event
dimWatcher.onDeleted(dimension.toKey());
dimWatcher.onDeleted(new ClientDimData(dimension));
//dimension.implode()??? -- more like delete, but yeah
return true;
}
@ -184,9 +183,7 @@ public class PocketManager
/**
* loads the dim data from the saved hashMap. Also handles compatibility with old saves, see OldSaveHandler
* @return
*/
@SuppressWarnings("unchecked")
private static void loadInternal()
{
// SenseiKiwi: This is a temporary function for testing purposes.
@ -196,10 +193,9 @@ public class PocketManager
DimensionManager.getCurrentSaveRootDirectory() != null)
{
System.out.println("Loading Dimensional Doors save data...");
File saveFile = new File(DimensionManager.getCurrentSaveRootDirectory() + "/dimdoors.dat");
//Missing code for converting the binary data in the file into an IOpaqueMessage
IDataMessage saveData;
setState(saveData);
/*File saveFile = new File(DimensionManager.getCurrentSaveRootDirectory() + "/dimdoors.dat");
setState(saveData);*/
System.out.println("Loaded successfully!");
}
}
@ -228,7 +224,7 @@ public class PocketManager
try
{
System.out.println("Writing Dimensional Doors save data...");
String tempPath = DimensionManager.getCurrentSaveRootDirectory() + "/dimdoors.tmp";
/*String tempPath = DimensionManager.getCurrentSaveRootDirectory() + "/dimdoors.tmp";
String savePath = DimensionManager.getCurrentSaveRootDirectory() + "/dimdoors.dat";
File tempFile = new File(tempPath);
File saveFile = new File(savePath);
@ -236,17 +232,17 @@ public class PocketManager
getState().writeToStream(writer);
writer.close();
saveFile.delete();
tempFile.renameTo(saveFile);
tempFile.renameTo(saveFile);*/
System.out.println("Saved successfully!");
}
catch (FileNotFoundException e)
/*catch (FileNotFoundException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
}*/
finally
{
isSaving = false;
@ -291,17 +287,17 @@ public class PocketManager
DDProperties properties = DDProperties.instance();
int dimensionID = DimensionManager.getNextFreeDimId();
DimensionManager.registerDimension(dimensionID, properties.PocketProviderID);
return registerDimension(dimensionID, parent, true, isDungeon);
return registerDimension(dimensionID, (InnerDimData) parent, true, isDungeon);
}
private static NewDimData registerDimension(int dimensionID, NewDimData parent, boolean isPocket, boolean isDungeon)
private static NewDimData registerDimension(int dimensionID, InnerDimData parent, boolean isPocket, boolean isDungeon)
{
if (dimensionData.containsKey(dimensionID))
{
throw new IllegalArgumentException("Cannot register a dimension with ID = " + dimensionID + " because it has already been registered.");
}
NewDimData dimension = new InnerDimData(dimensionID, parent, isPocket, isDungeon, dimWatcher, linkWatcher);
InnerDimData dimension = new InnerDimData(dimensionID, parent, isPocket, isDungeon, linkWatcher);
dimensionData.put(dimensionID, dimension);
return dimension;
}
@ -330,14 +326,18 @@ public class PocketManager
public static void unload()
{
save();
dimWatcher = null;
linkWatcher = null;
dimensionData = null;
unregisterPockets();
dimensionData.clear();
}
/*
* This isn't needed right now and it's causing me problems due to the iterator's generic type -_-
public static Iterable<NewDimData> getDimensions()
{
return dimensionData.values();
}
}*/
public static DimLink getLink(int x, int y, int z, World world)
{
@ -357,37 +357,96 @@ public class PocketManager
}
}
public static void registerDimWatcher(IUpdateWatcher watcher)
public static void registerDimWatcher(IUpdateWatcher<ClientDimData> watcher)
{
dimWatcher.registerReceiver(watcher);
}
public static boolean unregisterDimWatcher(IUpdateWatcher watcher)
public static boolean unregisterDimWatcher(IUpdateWatcher<ClientDimData> watcher)
{
return dimWatcher.unregisterReceiver(watcher);
}
public static void registerLinkWatcher(IUpdateWatcher watcher)
public static void registerLinkWatcher(IUpdateWatcher<Point4D> watcher)
{
linkWatcher.registerReceiver(watcher);
}
public static boolean unregisterLinkWatcher(IUpdateWatcher watcher)
public static boolean unregisterLinkWatcher(IUpdateWatcher<Point4D> watcher)
{
return linkWatcher.unregisterReceiver(watcher);
}
public static IDataMessage getState()
public static void writePacket(DataOutputStream output) throws IOException
{
// Write a very compact description of our dimensions and links to be sent to a client
Compactor.write(dimensionData.values(), output);
}
public static void setState(IDataMessage state)
public static void readPacket(DataInputStream input) throws IOException
{
if (isLoaded)
{
throw new IllegalStateException("Pocket dimensions have already been loaded!");
}
if (isLoading)
{
throw new IllegalStateException("Pocket dimensions are already loading!");
}
isLoading = true;
// Set up fields
dimensionData = new HashMap<Integer, InnerDimData>();
dimWatcher = new UpdateWatcherProxy<ClientDimData>();
linkWatcher = new UpdateWatcherProxy<Point4D>();
// Load compacted client-side dimension data
Compactor.readDimensions(input, new DimRegistrationCallback());
// Register pocket dimensions
DDProperties properties = DDProperties.instance();
registerPockets(properties);
isLoaded = true;
isLoading = false;
}
private static class DimRegistrationCallback implements IDimRegistrationCallback
{
// We use this class to provide Compactor with the ability to send us dim data without
// having to instantiate a bunch of data containers and without exposing an "unsafe"
// creation method for anyone to call. Integrity protection for the win! It's like
// exposing a private constructor ONLY to a very specific trusted class.
@Override
public NewDimData registerDimension(int dimensionID, int rootID)
{
// No need to raise events here since this code should only run on the client side
// We assume that the root dimension has already been registered to avoid dependency issues
InnerDimData root = dimensionData.get(rootID);
InnerDimData dimension;
if (rootID != dimensionID)
{
dimension = dimensionData.get(dimensionID);
if (dimension == null)
{
dimension = new InnerDimData(dimensionID, root);
dimensionData.put(dimension.id(), dimension);
}
}
else
{
if (root == null)
{
root = new InnerDimData(rootID);
dimensionData.put(root.id(), root);
}
dimension = root;
}
return dimension;
}
}
}

View file

@ -0,0 +1,83 @@
package StevenDimDoors.mod_pocketDim.helpers;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import StevenDimDoors.mod_pocketDim.core.DimLink;
import StevenDimDoors.mod_pocketDim.core.IDimRegistrationCallback;
import StevenDimDoors.mod_pocketDim.core.LinkTypes;
import StevenDimDoors.mod_pocketDim.core.NewDimData;
import StevenDimDoors.mod_pocketDim.util.Point4D;
public class Compactor
{
private static class DimComparator implements Comparator<NewDimData>
{
@Override
public int compare(NewDimData a, NewDimData b)
{
return a.id() - b.id();
}
}
public static void write(Collection<? extends NewDimData> values, DataOutputStream output) throws IOException
{
// SenseiKiwi: Just encode the data straight up for now. I'll implement fancier compression later.
output.writeInt(values.size());
for (NewDimData dimension : values)
{
output.writeInt(dimension.id());
output.writeInt(dimension.root().id());
output.writeInt(dimension.linkCount());
for (DimLink link : dimension.links())
{
Point4D.write(link.source(), output);
}
}
// Note to self: the root ID can be "compressed" by grouping
// dimensions by their root ID and then only sending it once
/*
// To compress the dimension IDs, we'll sort them by ID
// and write the _difference_ between their ID numbers.
NewDimData[] dimensions = new NewDimData[values.size()];
dimensions = values.toArray(dimensions);
Arrays.sort(dimensions, new DimComparator());
*/
}
public static void readDimensions(DataInputStream input, IDimRegistrationCallback callback) throws IOException
{
// Read in the dimensions one by one. Make sure we register root dimensions before
// attempting to register the dimensions under them.
HashSet<Integer> rootIDs = new HashSet<Integer>();
int dimCount = input.readInt();
for (int k = 0; k < dimCount; k++)
{
int id = input.readInt();
int rootID = input.readInt();
if (rootIDs.add(rootID))
{
callback.registerDimension(rootID, rootID);
}
// Don't check if (id != rootID) - we want to retrieve the reference anyway
NewDimData dimension = callback.registerDimension(id, rootID);
int linkCount = input.readInt();
for (int h = 0; h < linkCount; h++)
{
Point4D source = Point4D.read(input);
dimension.createLink(source.getX(), source.getY(), source.getZ(), LinkTypes.UNKNOWN);
}
}
}
}

View file

@ -1,116 +0,0 @@
package StevenDimDoors.mod_pocketDim.messages;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import StevenDimDoors.mod_pocketDim.core.DimLink;
import StevenDimDoors.mod_pocketDim.core.NewDimData;
import StevenDimDoors.mod_pocketDim.core.NewDimData.InnerDimLink;
import StevenDimDoors.mod_pocketDim.dungeon.DungeonData;
import StevenDimDoors.mod_pocketDim.util.Point4D;
import com.google.common.collect.ImmutableList;
public class DimMessageBuilder implements IMessageBuilder<NewDimData>
{
public static class DimMessage implements IDataMessage
{
//We'll use public fields here since this is a data container object and all the fields are immutable
//We will not transfer dungeon, link data, or any data on child dimensions
//As far as I can tell, the children will handle updating their parents anyway
public final int ID;
public final boolean IsDungeon;
public final boolean IsFilled;
public final int Depth;
public final int PackDepth;
public final Integer ParentID;
public final int RootID;
public final Point4D Origin;
public final int Orientation;
private DimMessage(NewDimData dimension)
{
ID = dimension.id();
IsDungeon = dimension.isDungeon();
IsFilled = dimension.isFilled();
Depth = dimension.depth();
PackDepth = dimension.packDepth();
ParentID = (dimension.parent() != null) ? dimension.parent().id() : null;
RootID = dimension.root().id();
Origin = dimension.origin();
Orientation = dimension.orientation();
}
private DimMessage(DataInputStream stream) throws IOException
{
ID = stream.readInt();
IsDungeon = stream.readBoolean();
IsFilled = stream.readBoolean();
Depth = stream.readInt();
PackDepth = stream.readInt();
ParentID = stream.
}
@Override
public void writeToStream(DataOutputStream stream) throws IOException
{
//Write a flag indicating that this is a full message and not a key
stream.writeBoolean(true);
}
}
public static class DimKeyMessage implements IDataMessage
{
//We'll use public fields here since this is a data container object and all the fields are immutable
public final int ID;
private DimKeyMessage(NewDimData dimension)
{
ID = dimension.id();
}
private DimKeyMessage(DataInputStream stream) throws IOException
{
ID = stream.readInt();
}
@Override
public void writeToStream(DataOutputStream stream) throws IOException
{
//Write a flag indicating that this is a key
stream.writeBoolean(false);
stream.writeInt(ID);
}
}
@Override
public IDataMessage createKey(NewDimData target)
{
return new DimKeyMessage(target);
}
@Override
public IDataMessage createMessage(NewDimData target)
{
return new DimMessage(target);
}
@Override
public IDataMessage read(DataInputStream source) throws IOException
{
//Check whether the message is a full message or just a key
if (source.readBoolean())
{
return new DimMessage(source);
}
else
{
return new DimKeyMessage(source);
}
}
}

View file

@ -1,9 +0,0 @@
package StevenDimDoors.mod_pocketDim.messages;
import java.io.DataOutputStream;
import java.io.IOException;
public interface IDataMessage
{
public void writeToStream(DataOutputStream stream) throws IOException;
}

View file

@ -1,11 +0,0 @@
package StevenDimDoors.mod_pocketDim.messages;
import java.io.DataInputStream;
import java.io.IOException;
public interface IMessageBuilder<T>
{
public IDataMessage createKey(T target);
public IDataMessage createMessage(T target);
public IDataMessage read(DataInputStream source) throws IOException;
}

View file

@ -1,8 +0,0 @@
package StevenDimDoors.mod_pocketDim.messages;
public interface IUpdateWatcher
{
public void onCreated(IDataMessage message);
public void onUpdated(IDataMessage message);
public void onDeleted(IDataMessage message);
}

View file

@ -1,134 +0,0 @@
package StevenDimDoors.mod_pocketDim.messages;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import StevenDimDoors.mod_pocketDim.core.DimLink;
import StevenDimDoors.mod_pocketDim.util.Point4D;
import com.google.common.collect.ImmutableList;
public class LinkMessageBuilder implements IMessageBuilder<DimLink>
{
public static class LinkMessage implements IDataMessage
{
//We'll use public fields here since this is a data container object and all the fields are immutable
public final Point4D Source;
public final Point4D Destination;
public final int LinkType;
public final Point4D Parent;
public final ImmutableList<Point4D> Children;
private LinkMessage(DimLink link)
{
// TODO: In the case that a child's parent has been removed but the rest of the group still exists,
// this group bond will be lost to this link on the client side. Currently, that's not a problem since
// destination data and groups don't matter to the client, but it's something to think about later.
Source = link.source();
Destination = link.destination();
LinkType = link.linkType();
Parent = link.parent().source();
ImmutableList.Builder<Point4D> builder = new ImmutableList.Builder<Point4D>();
for (DimLink child : link.children())
{
builder.add(child.source());
}
Children = builder.build();
}
private LinkMessage(DataInputStream stream) throws IOException
{
Source = Point4D.read(stream);
Parent = Point4D.read(stream);
if (Parent == null)
{
Destination = Point4D.read(stream);
LinkType = stream.readInt();
}
else
{
Destination = null;
LinkType = -1;
}
int childCount = stream.readInt();
ImmutableList.Builder<Point4D> builder = new ImmutableList.Builder<Point4D>();
for (int k = 0; k < childCount; k++)
{
builder.add(Point4D.read(stream));
}
Children = builder.build();
}
@Override
public void writeToStream(DataOutputStream stream) throws IOException
{
//Write a flag indicating that this is a full message and not a key
stream.writeBoolean(true);
Point4D.write(Source, stream);
Point4D.write(Parent, stream);
//A link only has its own destination information if it has no parent to provide it
if (Parent == null)
{
Point4D.write(Destination, stream);
stream.writeInt(LinkType);
}
stream.writeInt(Children.size());
for (Point4D child : Children)
{
Point4D.write(child, stream);
}
}
}
public static class LinkKeyMessage implements IDataMessage
{
//We'll use public fields here since this is a data container object and all the fields are immutable
public final Point4D Source;
private LinkKeyMessage(DimLink link)
{
Source = link.source();
}
private LinkKeyMessage(DataInputStream stream) throws IOException
{
Source = Point4D.read(stream);
}
@Override
public void writeToStream(DataOutputStream stream) throws IOException
{
//Write a flag indicating that this is a key
stream.writeBoolean(false);
Point4D.write(Source, stream);
}
}
@Override
public IDataMessage createKey(DimLink target)
{
return new LinkKeyMessage(target);
}
@Override
public IDataMessage createMessage(DimLink target)
{
return new LinkMessage(target);
}
@Override
public IDataMessage read(DataInputStream source) throws IOException
{
//Check whether the message is a full message or just a key
if (source.readBoolean())
{
return new LinkMessage(source);
}
else
{
return new LinkKeyMessage(source);
}
}
}

View file

@ -1,51 +0,0 @@
package StevenDimDoors.mod_pocketDim.messages;
import java.util.ArrayList;
import java.util.List;
public class UpdateWatcherProxy implements IUpdateWatcher
{
private List<IUpdateWatcher> watchers;
public UpdateWatcherProxy()
{
watchers = new ArrayList<IUpdateWatcher>();
}
@Override
public void onCreated(IDataMessage message)
{
for (IUpdateWatcher receiver : watchers)
{
receiver.onCreated(message);
}
}
@Override
public void onUpdated(IDataMessage message)
{
for (IUpdateWatcher receiver : watchers)
{
receiver.onUpdated(message);
}
}
@Override
public void onDeleted(IDataMessage message)
{
for (IUpdateWatcher receiver : watchers)
{
receiver.onDeleted(message);
}
}
public void registerReceiver(IUpdateWatcher receiver)
{
watchers.add(receiver);
}
public boolean unregisterReceiver(IUpdateWatcher receiver)
{
return watchers.remove(receiver);
}
}

View file

@ -0,0 +1,37 @@
package StevenDimDoors.mod_pocketDim.watcher;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import StevenDimDoors.mod_pocketDim.core.NewDimData;
public class ClientDimData
{
//We'll use public fields since this is just a data container and it's immutable
public final int ID;
public final int RootID;
public ClientDimData(int id, int rootID)
{
ID = id;
RootID = rootID;
}
public ClientDimData(NewDimData dimension)
{
ID = dimension.id();
RootID = dimension.root().id();
}
public void write(DataOutputStream output) throws IOException
{
output.writeInt(ID);
output.writeInt(RootID);
}
public static ClientDimData read(DataInputStream input) throws IOException
{
return new ClientDimData(input.readInt(), input.readInt());
}
}

View file

@ -0,0 +1,7 @@
package StevenDimDoors.mod_pocketDim.watcher;
public interface IUpdateWatcher<T>
{
public void onCreated(T message);
public void onDeleted(T message);
}

View file

@ -0,0 +1,42 @@
package StevenDimDoors.mod_pocketDim.watcher;
import java.util.ArrayList;
import java.util.List;
public class UpdateWatcherProxy<T> implements IUpdateWatcher<T>
{
private List<IUpdateWatcher<T>> watchers;
public UpdateWatcherProxy()
{
watchers = new ArrayList<IUpdateWatcher<T>>();
}
@Override
public void onCreated(T message)
{
for (IUpdateWatcher<T> receiver : watchers)
{
receiver.onCreated(message);
}
}
@Override
public void onDeleted(T message)
{
for (IUpdateWatcher<T> receiver : watchers)
{
receiver.onDeleted(message);
}
}
public void registerReceiver(IUpdateWatcher<T> receiver)
{
watchers.add(receiver);
}
public boolean unregisterReceiver(IUpdateWatcher<T> receiver)
{
return watchers.remove(receiver);
}
}