feat: implement usercache system

This commit is contained in:
LordMZTE 2024-04-30 19:41:24 +02:00
parent 4c4820693e
commit fcab7822fb
Signed by: LordMZTE
GPG key ID: B64802DC33A64FF6
9 changed files with 384 additions and 1 deletions

View file

@ -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()
}
}
}
}

View 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);
}
}

View file

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

View 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);
}
}

View file

@ -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) {}
}

View file

@ -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) {}
}
}
}

View file

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

View file

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

View file

@ -0,0 +1 @@
public net.minecraft.world.storage.SaveHandler field_75771_c # playersDirectory