package openmods.network;
import com.google.common.base.Preconditions;
import com.google.common.io.Closer;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.LoaderState;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.network.FMLNetworkEvent.ClientDisconnectionFromServerEvent;
import cpw.mods.fml.common.network.NetworkHandshakeEstablished;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.network.internal.FMLProxyPacket;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import openmods.Log;
import openmods.OpenMods;
import openmods.datastore.DataStoreBuilder;
import openmods.datastore.DataStoreKey;
import openmods.datastore.DataStoreManager;
import openmods.datastore.DataStoreReader;
import openmods.datastore.DataStoreWrapper;
import openmods.datastore.DataStoreWriter;
public class IdSyncManager extends DataStoreManager {
private static final String CHANNEL_NAME = "OpenMods|I";
public static final IdSyncManager INSTANCE = new IdSyncManager();
@Sharable
private class InboundHandler extends SimpleChannelInboundHandler<FMLProxyPacket> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, FMLProxyPacket msg) throws Exception {
ByteBuf buf = msg.payload();
try {
decodeIds(buf);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof NetworkHandshakeEstablished) {
Log.debug("Sending id data for player: %s", OpenMods.proxy.getPlayerFromHandler(((NetworkHandshakeEstablished)evt).netHandler));
sendAllIds(ctx);
} else {
ctx.fireUserEventTriggered(evt);
}
}
}
private static FMLProxyPacket serializeToPacket(DataStoreKey<?, ?> key, DataStoreWriter<?, ?> writer) {
ByteBuf payload = Unpooled.buffer();
Closer closer = Closer.create();
try {
try {
OutputStream raw = closer.register(new ByteBufOutputStream(payload));
OutputStream compressed = closer.register(new GZIPOutputStream(raw));
DataOutput output = new DataOutputStream(compressed);
output.writeUTF(key.id);
writer.write(output);
} finally {
closer.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return new FMLProxyPacket(payload.copy(), CHANNEL_NAME);
}
private IdSyncManager() {
NetworkRegistry.INSTANCE.newChannel(CHANNEL_NAME, new InboundHandler());
}
public <K, V> DataStoreBuilder<K, V> createDataStore(String domain, String id, Class<? extends K> keyClass, Class<? extends V> valueClass) {
final String fullId = domain + ":" + id;
return createDataStore(fullId, keyClass, valueClass);
}
@Override
public <K, V> DataStoreBuilder<K, V> createDataStore(String id, Class<? extends K> keyClass, Class<? extends V> valueClass) {
Preconditions.checkState(!Loader.instance().hasReachedState(LoaderState.POSTINITIALIZATION), "This method cannot be called in post-initialization state and later");
return super.createDataStore(id, keyClass, valueClass);
}
private void sendAllIds(ChannelHandlerContext ctx) {
validate();
for (Map.Entry<DataStoreKey<?, ?>, DataStoreWrapper<?, ?>> e : dataStoreMeta.entrySet()) {
FMLProxyPacket packet = serializeToPacket(e.getKey(), e.getValue().createWriter());
ctx.write(packet);
}
}
private void decodeIds(ByteBuf buf) throws IOException {
Closer closer = Closer.create();
try {
InputStream raw = closer.register(new ByteBufInputStream(buf));
InputStream compressed = closer.register(new GZIPInputStream(raw));
DataInput input = new DataInputStream(compressed);
String keyId = input.readUTF();
Log.debug("Received data store for key %s, packet size = %d", keyId, buf.writerIndex());
DataStoreWrapper<?, ?> wrapper = getDataStoreMeta(keyId);
DataStoreReader<?, ?> reader = wrapper.createReader();
reader.read(input);
} finally {
closer.close();
}
}
@SubscribeEvent
public void onDisconnect(ClientDisconnectionFromServerEvent evt) {
Log.debug("Disconnected, restoring local data");
activateLocalData();
}
public void finishLoading() {
validate();
FMLCommonHandler.instance().bus().register(this);
}
}