feat: implement usercache system
This commit is contained in:
parent
4c4820693e
commit
06fab6b936
63
src/main/java/net/anvilcraft/anvillib/AnvilLib.java
Normal file
63
src/main/java/net/anvilcraft/anvillib/AnvilLib.java
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package net.anvilcraft.anvillib;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.FMLCommonHandler;
|
||||||
|
import cpw.mods.fml.common.Mod;
|
||||||
|
import cpw.mods.fml.common.Mod.EventHandler;
|
||||||
|
import cpw.mods.fml.common.SidedProxy;
|
||||||
|
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||||
|
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||||
|
import cpw.mods.fml.common.event.FMLServerStoppedEvent;
|
||||||
|
import cpw.mods.fml.common.network.NetworkRegistry;
|
||||||
|
import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import net.anvilcraft.anvillib.network.PacketUpdateUserCache;
|
||||||
|
import net.anvilcraft.anvillib.proxy.CommonProxy;
|
||||||
|
import net.anvilcraft.anvillib.usercache.UserCache;
|
||||||
|
import net.anvilcraft.anvillib.usercache.UserCacheEventHandler;
|
||||||
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
|
|
||||||
|
@Mod(modid = "anvillib", version = "{VERSION}", name = "AnvilLib")
|
||||||
|
public class AnvilLib {
|
||||||
|
public static final Logger LOGGER = LogManager.getLogger("AnvilLib");
|
||||||
|
|
||||||
|
@SidedProxy(
|
||||||
|
modId = "anvillib",
|
||||||
|
serverSide = "net.anvilcraft.anvillib.proxy.CommonProxy",
|
||||||
|
clientSide = "net.anvilcraft.anvillib.proxy.ClientProxy"
|
||||||
|
)
|
||||||
|
public static CommonProxy proxy;
|
||||||
|
|
||||||
|
public static SimpleNetworkWrapper channel;
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public static void preInit(FMLPreInitializationEvent ev) {
|
||||||
|
proxy.loadUserCache(UserCache.INSTANCE);
|
||||||
|
UserCacheEventHandler uceh = new UserCacheEventHandler();
|
||||||
|
MinecraftForge.EVENT_BUS.register(uceh);
|
||||||
|
FMLCommonHandler.instance().bus().register(uceh);
|
||||||
|
|
||||||
|
Runtime.getRuntime().addShutdownHook(
|
||||||
|
new Thread(() -> proxy.saveUserCache(UserCache.INSTANCE))
|
||||||
|
);
|
||||||
|
|
||||||
|
channel = NetworkRegistry.INSTANCE.newSimpleChannel("anvillib");
|
||||||
|
int pktid = 0;
|
||||||
|
channel.registerMessage(
|
||||||
|
PacketUpdateUserCache.Handler.class,
|
||||||
|
PacketUpdateUserCache.class,
|
||||||
|
pktid++,
|
||||||
|
Side.CLIENT
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public static void init(FMLInitializationEvent ev) {}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public static void shutdown(FMLServerStoppedEvent ev) {
|
||||||
|
proxy.saveUserCache(UserCache.INSTANCE);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package net.anvilcraft.anvillib.network;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.network.simpleimpl.IMessage;
|
||||||
|
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler;
|
||||||
|
import cpw.mods.fml.common.network.simpleimpl.MessageContext;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import net.anvilcraft.anvillib.usercache.UserCache;
|
||||||
|
|
||||||
|
public class PacketUpdateUserCache implements IMessage {
|
||||||
|
public Map<UUID, String> entries;
|
||||||
|
|
||||||
|
public PacketUpdateUserCache(Map<UUID, String> entries) {
|
||||||
|
this.entries = entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketUpdateUserCache() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void toBytes(ByteBuf buf) {
|
||||||
|
for (Entry<UUID, String> ent : this.entries.entrySet()) {
|
||||||
|
buf.writeLong(ent.getKey().getMostSignificantBits());
|
||||||
|
buf.writeLong(ent.getKey().getLeastSignificantBits());
|
||||||
|
|
||||||
|
buf.writeInt(ent.getValue().length());
|
||||||
|
buf.writeBytes(ent.getValue().getBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fromBytes(ByteBuf buf) {
|
||||||
|
this.entries = new HashMap<>();
|
||||||
|
|
||||||
|
while (buf.readableBytes() > 0) {
|
||||||
|
UUID id = new UUID(buf.readLong(), buf.readLong());
|
||||||
|
|
||||||
|
byte[] nameBytes = new byte[buf.readInt()];
|
||||||
|
buf.readBytes(nameBytes);
|
||||||
|
|
||||||
|
String name = new String(nameBytes);
|
||||||
|
|
||||||
|
this.entries.put(id, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Handler
|
||||||
|
implements IMessageHandler<PacketUpdateUserCache, IMessage> {
|
||||||
|
@Override
|
||||||
|
public IMessage onMessage(PacketUpdateUserCache pkt, MessageContext arg1) {
|
||||||
|
UserCache.INSTANCE.users.putAll(pkt.entries);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
src/main/java/net/anvilcraft/anvillib/proxy/ClientProxy.java
Normal file
16
src/main/java/net/anvilcraft/anvillib/proxy/ClientProxy.java
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package net.anvilcraft.anvillib.proxy;
|
||||||
|
|
||||||
|
import net.anvilcraft.anvillib.usercache.ClientCacheManager;
|
||||||
|
import net.anvilcraft.anvillib.usercache.UserCache;
|
||||||
|
|
||||||
|
public class ClientProxy extends CommonProxy {
|
||||||
|
@Override
|
||||||
|
public void loadUserCache(UserCache cache) {
|
||||||
|
ClientCacheManager.loadCacheInto(cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveUserCache(UserCache cache) {
|
||||||
|
ClientCacheManager.serializeCache(cache);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package net.anvilcraft.anvillib.proxy;
|
||||||
|
|
||||||
|
import net.anvilcraft.anvillib.usercache.UserCache;
|
||||||
|
|
||||||
|
public class CommonProxy {
|
||||||
|
public void loadUserCache(UserCache cache) {}
|
||||||
|
|
||||||
|
public void saveUserCache(UserCache cache) {}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
package net.anvilcraft.anvillib.usercache;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
import net.anvilcraft.anvillib.AnvilLib;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class loads the client's serialized user cache and adds it to the cache instance.
|
||||||
|
*/
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public class ClientCacheManager {
|
||||||
|
public static final String FILE_PATH = "anvillib_usercache";
|
||||||
|
|
||||||
|
public static void loadCacheInto(UserCache cache) {
|
||||||
|
BufferedReader reader = null;
|
||||||
|
try {
|
||||||
|
reader = new BufferedReader(new FileReader(FILE_PATH));
|
||||||
|
|
||||||
|
int linen = 0;
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
linen++;
|
||||||
|
Pair<UUID, String> parsed = parseLine(line);
|
||||||
|
if (parsed == null) {
|
||||||
|
AnvilLib.LOGGER.warn(
|
||||||
|
"Usercache contains invalid line @ {}, skipping", linen
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.addEntry(parsed.getLeft(), parsed.getRight());
|
||||||
|
}
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
AnvilLib.LOGGER.warn("Usercache file does not exist.");
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (reader != null)
|
||||||
|
reader.close();
|
||||||
|
} catch (IOException alec) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Pair<UUID, String> parseLine(String line) {
|
||||||
|
String[] splits = line.split("=", 2);
|
||||||
|
if (splits.length != 2)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
UUID id;
|
||||||
|
try {
|
||||||
|
id = UUID.fromString(splits[0]);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pair.of(id, splits[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void serializeCache(UserCache cache) {
|
||||||
|
AnvilLib.LOGGER.debug("Saving usercache");
|
||||||
|
File outfile = new File(FILE_PATH);
|
||||||
|
Writer writer = null;
|
||||||
|
try {
|
||||||
|
outfile.createNewFile();
|
||||||
|
writer = new BufferedWriter(new FileWriter(outfile));
|
||||||
|
for (Entry<UUID, String> ent : cache.users.entrySet()) {
|
||||||
|
writer.append(ent.getKey().toString() + "=" + ent.getValue() + "\n");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (writer != null)
|
||||||
|
writer.close();
|
||||||
|
} catch (IOException alec) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package net.anvilcraft.anvillib.usercache;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A singleton data structure that serves as a lookup table from user UUIDs to their usernames.
|
||||||
|
* It is serialized on the client and in worlds and is synched to the client upon joining a server.
|
||||||
|
*/
|
||||||
|
public class UserCache {
|
||||||
|
public static final UserCache INSTANCE = new UserCache();
|
||||||
|
public Map<UUID, String> users = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new entry to the usercache.
|
||||||
|
*
|
||||||
|
* @param id The UUID of the player.
|
||||||
|
* @param name The name of the player.
|
||||||
|
*/
|
||||||
|
public void addEntry(UUID id, String name) {
|
||||||
|
this.users.put(id, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a player to the usercache.
|
||||||
|
*
|
||||||
|
* @param player Player Entity to add.
|
||||||
|
*/
|
||||||
|
public void addEntry(EntityPlayer player) {
|
||||||
|
this.addEntry(player.getUniqueID(), player.getCommandSenderName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a username given the user's UUID.
|
||||||
|
*
|
||||||
|
* @param id The UUID of the player.
|
||||||
|
*
|
||||||
|
* @return The player's Username or null if it is not known.
|
||||||
|
*/
|
||||||
|
public String getCached(UUID id) {
|
||||||
|
return this.users.get(id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package net.anvilcraft.anvillib.usercache;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent;
|
||||||
|
import cpw.mods.fml.common.network.simpleimpl.IMessage;
|
||||||
|
import net.anvilcraft.anvillib.AnvilLib;
|
||||||
|
import net.anvilcraft.anvillib.network.PacketUpdateUserCache;
|
||||||
|
import net.minecraft.entity.player.EntityPlayerMP;
|
||||||
|
import net.minecraft.nbt.CompressedStreamTools;
|
||||||
|
import net.minecraft.nbt.NBTTagCompound;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.world.storage.SaveHandler;
|
||||||
|
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||||
|
import net.minecraftforge.event.world.WorldEvent;
|
||||||
|
|
||||||
|
public class UserCacheEventHandler {
|
||||||
|
@SubscribeEvent
|
||||||
|
public void onWorldSave(WorldEvent.Save ev) {
|
||||||
|
AnvilLib.proxy.saveUserCache(UserCache.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void onPlayerSave(PlayerEvent.SaveToFile ev) {
|
||||||
|
ev.entityPlayer.getEntityData().setString(
|
||||||
|
"anvillib:name", ev.entityPlayer.getCommandSenderName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void onWorldLoad(WorldEvent.Load ev) {
|
||||||
|
if (ev.world.provider.dimensionId != 0
|
||||||
|
|| !(ev.world.getSaveHandler() instanceof SaveHandler))
|
||||||
|
return;
|
||||||
|
|
||||||
|
int loaded = 0;
|
||||||
|
SaveHandler sh = (SaveHandler) ev.world.getSaveHandler();
|
||||||
|
for (String playerdat : sh.getAvailablePlayerDat()) {
|
||||||
|
try {
|
||||||
|
File f = new File(sh.playersDirectory, playerdat + ".dat");
|
||||||
|
|
||||||
|
UUID id = UUID.fromString(playerdat);
|
||||||
|
NBTTagCompound tag
|
||||||
|
= CompressedStreamTools.readCompressed(new FileInputStream(f));
|
||||||
|
|
||||||
|
String name = tag.getCompoundTag("ForgeData").getString("anvillib:name");
|
||||||
|
if (!name.isEmpty()) {
|
||||||
|
UserCache.INSTANCE.addEntry(id, name);
|
||||||
|
loaded++;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AnvilLib.LOGGER.info("Loaded {} usercache entries from player dat", loaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void onPlayerJoin(PlayerLoggedInEvent ev) {
|
||||||
|
UserCache.INSTANCE.addEntry(ev.player);
|
||||||
|
|
||||||
|
if (!(ev.player instanceof EntityPlayerMP)
|
||||||
|
|| ((EntityPlayerMP) ev.player).getPlayerIP().equals("local"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
AnvilLib.channel.sendTo(
|
||||||
|
new PacketUpdateUserCache(UserCache.INSTANCE.users),
|
||||||
|
(EntityPlayerMP) ev.player
|
||||||
|
);
|
||||||
|
|
||||||
|
IMessage updatePkt = new PacketUpdateUserCache(
|
||||||
|
ImmutableMap.of(ev.player.getUniqueID(), ev.player.getCommandSenderName())
|
||||||
|
);
|
||||||
|
for (EntityPlayerMP pl : (List<EntityPlayerMP>) MinecraftServer.getServer()
|
||||||
|
.getConfigurationManager()
|
||||||
|
.playerEntityList) {
|
||||||
|
if (pl == ev.player)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
AnvilLib.channel.sendTo(updatePkt, pl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
src/main/resources/META-INF/anvillib_at.cfg
Normal file
1
src/main/resources/META-INF/anvillib_at.cfg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
public net.minecraft.world.storage.SaveHandler field_75771_c # playersDirectory
|
Loading…
Reference in a new issue