feat: implement usercache system
This commit is contained in:
parent
4c4820693e
commit
fcab7822fb
9 changed files with 384 additions and 1 deletions
|
@ -36,6 +36,9 @@ configurations {
|
|||
minecraft {
|
||||
version = "1.7.10-10.13.4.1614-1.7.10"
|
||||
runDir = "run"
|
||||
|
||||
replaceIn "net/anvilcraft/anvillib/AnvilLib.java"
|
||||
replace "{VERSION}", project.version
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
@ -55,6 +58,10 @@ jar {
|
|||
duplicatesStrategy = 'exclude'
|
||||
exclude 'LICENSE.txt', 'META-INF/MANIFSET.MF', 'META-INF/maven/**', 'META-INF/*.RSA', 'META-INF/*.SF', 'META-INF/services/*.Processor', 'META-INF/versions/**'
|
||||
}
|
||||
|
||||
manifest {
|
||||
attributes "FMLAT": "anvillib_at.cfg"
|
||||
}
|
||||
}
|
||||
|
||||
processResources {
|
||||
|
@ -102,4 +109,4 @@ publishing {
|
|||
mavenLocal()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
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