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

@@ -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;
}
}
}