From 2640c2b0459b87f0702e595f44e414ae35db07b5 Mon Sep 17 00:00:00 2001 From: LAX1DUDE Date: Sun, 5 Jun 2022 00:03:04 -0700 Subject: [PATCH] Added multiplayer and skins --- bukkit/readme.txt | 10 + .../beta/server/EaglercraftConfig.java | 119 + .../beta/server/EaglercraftServer.java | 94 +- .../beta/server/NetEaglerServerHandler.java | 56 + .../beta/server/Packet69EaglercraftData.java | 58 + .../beta/server/PasswordManager.java | 39 +- .../commands/CommandChangePassword.java | 12 +- .../server/commands/CommandClearPassword.java | 21 +- .../server/commands/CommandListPasswords.java | 15 +- .../commands/CommandPasswordExpires.java | 4 + .../beta/server/commands/CommandRegister.java | 70 + .../server/commands/CommandRenewPassword.java | 164 +- .../server/commands/CommandSetPassword.java | 45 +- .../EaglercraftVanillaNetworkManager.java | 2 +- .../EaglercraftWebsocketListenerThread.java | 5 +- .../EaglercraftWebsocketNetworkManager.java | 61 +- .../java/net/minecraft/server/NetHandler.java | 192 + .../net/minecraft/server/NetLoginHandler.java | 126 +- .../net/minecraft/server/NetworkManager.java | 18 +- .../net/minecraft/server/PacketRegister.java | 9 + .../net/minecraft/server/PlayerInstance.java | 184 + .../java/net/minecraft/server/PlayerList.java | 128 + .../net/minecraft/server/PlayerManager.java | 181 + .../main/resources/default_eagler_config.yml | 11 + javascript/Singleplayer_Offline_Download.html | 10189 ---------------- lwjgl-rundir/resources/gui/gui.png | Bin 14210 -> 16479 bytes lwjgl-rundir/resources/lang/en_US.lang | 6 + lwjgl-rundir/resources/misc/laxcape.png | Bin 0 -> 511 bytes .../resources/skins/01.default_steve.png | Bin 0 -> 1832 bytes .../resources/skins/02.tennis_steve.png | Bin 0 -> 1755 bytes .../resources/skins/03.tuxedo_steve.png | Bin 0 -> 4339 bytes .../resources/skins/04.athlete_steve.png | Bin 0 -> 4234 bytes .../resources/skins/05.cyclist_steve.png | Bin 0 -> 1834 bytes .../resources/skins/06.boxer_steve.png | Bin 0 -> 2347 bytes .../resources/skins/07.prisoner_steve.png | Bin 0 -> 4251 bytes .../resources/skins/08.scottish_steve.png | Bin 0 -> 4647 bytes lwjgl-rundir/resources/skins/09.dev_steve.png | Bin 0 -> 1630 bytes lwjgl-rundir/resources/skins/10.herobrine.png | Bin 0 -> 1605 bytes lwjgl-rundir/resources/skins/11.slime.png | Bin 0 -> 1406 bytes lwjgl-rundir/resources/skins/12.trump.png | Bin 0 -> 1522 bytes lwjgl-rundir/resources/skins/13.notch.png | Bin 0 -> 950 bytes lwjgl-rundir/resources/skins/14.creeper.png | Bin 0 -> 3145 bytes lwjgl-rundir/resources/skins/15.zombie.png | Bin 0 -> 2472 bytes lwjgl-rundir/resources/skins/16.pig.png | Bin 0 -> 2790 bytes lwjgl-rundir/resources/skins/17.squid.png | Bin 0 -> 3082 bytes lwjgl-rundir/resources/skins/18.mooshroom.png | Bin 0 -> 1679 bytes .../eaglercraft/anvil/SaveFormatOld.java | 2 +- .../lax1dude/eaglercraft/ConfigConstants.java | 2 +- .../lax1dude/eaglercraft/EaglerProfile.java | 389 + .../eaglercraft/GuiScreenEditProfile.java | 401 + .../lax1dude/eaglercraft/TextureLocation.java | 12 +- .../eaglercraft/WebsocketNetworkManager.java | 4 + .../beta/EaglercraftSaveManager.java | 3 +- .../java/net/minecraft/client/Minecraft.java | 29 +- .../minecraft/src/EntityOtherPlayerMP.java | 1 + .../java/net/minecraft/src/EntityPlayer.java | 1 + .../net/minecraft/src/EntityPlayerSP.java | 4 - .../net/minecraft/src/EntityRenderer.java | 4 +- .../java/net/minecraft/src/GuiConnecting.java | 2 + .../java/net/minecraft/src/GuiMainMenu.java | 7 +- .../net/minecraft/src/GuiSelectWorld.java | 2 +- .../java/net/minecraft/src/ISaveFormat.java | 2 +- .../java/net/minecraft/src/ItemRenderer.java | 10 +- .../java/net/minecraft/src/ModelBiped.java | 15 + .../net/minecraft/src/NBTTagCompound.java | 8 + .../net/minecraft/src/NetClientHandler.java | 23 +- .../java/net/minecraft/src/NetHandler.java | 4 + src/main/java/net/minecraft/src/Packet.java | 1 + .../src/Packet69EaglercraftData.java | 47 + .../java/net/minecraft/src/RenderEngine.java | 19 +- .../net/minecraft/src/RenderFallingSand.java | 1 + .../java/net/minecraft/src/RenderGlobal.java | 42 +- .../java/net/minecraft/src/RenderPlayer.java | 50 +- .../java/net/minecraft/src/WorldClient.java | 9 + .../java/net/lax1dude/eaglercraft/Client.java | 13 +- 75 files changed, 2542 insertions(+), 10384 deletions(-) create mode 100644 bukkit/readme.txt create mode 100644 bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/EaglercraftConfig.java create mode 100644 bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/NetEaglerServerHandler.java create mode 100644 bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/Packet69EaglercraftData.java create mode 100644 bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandRegister.java create mode 100644 bukkit/src/main/java/net/minecraft/server/NetHandler.java create mode 100644 bukkit/src/main/java/net/minecraft/server/PacketRegister.java create mode 100644 bukkit/src/main/java/net/minecraft/server/PlayerInstance.java create mode 100644 bukkit/src/main/java/net/minecraft/server/PlayerList.java create mode 100644 bukkit/src/main/java/net/minecraft/server/PlayerManager.java create mode 100644 bukkit/src/main/resources/default_eagler_config.yml delete mode 100644 javascript/Singleplayer_Offline_Download.html create mode 100644 lwjgl-rundir/resources/misc/laxcape.png create mode 100644 lwjgl-rundir/resources/skins/01.default_steve.png create mode 100644 lwjgl-rundir/resources/skins/02.tennis_steve.png create mode 100644 lwjgl-rundir/resources/skins/03.tuxedo_steve.png create mode 100644 lwjgl-rundir/resources/skins/04.athlete_steve.png create mode 100644 lwjgl-rundir/resources/skins/05.cyclist_steve.png create mode 100644 lwjgl-rundir/resources/skins/06.boxer_steve.png create mode 100644 lwjgl-rundir/resources/skins/07.prisoner_steve.png create mode 100644 lwjgl-rundir/resources/skins/08.scottish_steve.png create mode 100644 lwjgl-rundir/resources/skins/09.dev_steve.png create mode 100644 lwjgl-rundir/resources/skins/10.herobrine.png create mode 100644 lwjgl-rundir/resources/skins/11.slime.png create mode 100644 lwjgl-rundir/resources/skins/12.trump.png create mode 100644 lwjgl-rundir/resources/skins/13.notch.png create mode 100644 lwjgl-rundir/resources/skins/14.creeper.png create mode 100644 lwjgl-rundir/resources/skins/15.zombie.png create mode 100644 lwjgl-rundir/resources/skins/16.pig.png create mode 100644 lwjgl-rundir/resources/skins/17.squid.png create mode 100644 lwjgl-rundir/resources/skins/18.mooshroom.png create mode 100644 src/main/java/net/lax1dude/eaglercraft/EaglerProfile.java create mode 100644 src/main/java/net/lax1dude/eaglercraft/GuiScreenEditProfile.java create mode 100644 src/main/java/net/minecraft/src/Packet69EaglercraftData.java diff --git a/bukkit/readme.txt b/bukkit/readme.txt new file mode 100644 index 0000000..b4796db --- /dev/null +++ b/bukkit/readme.txt @@ -0,0 +1,10 @@ +To compile: + +1. Link "src/main/java" as a source folder +2. Link "src/main/resources" as a source folder +3. Add "server/craftbukkit-bin.jar" to the build path as a dependency +4. Add "server/java_websocket.jar" to the build path as a dependency +5. Set "watchdog.WatchDog" as the main class +6. Export the project as a runnable jar file + +You cannot run the project from within the IDE, you must export it every time as a runnable jar file for both testing and production. \ No newline at end of file diff --git a/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/EaglercraftConfig.java b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/EaglercraftConfig.java new file mode 100644 index 0000000..8cb49ec --- /dev/null +++ b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/EaglercraftConfig.java @@ -0,0 +1,119 @@ +package net.lax1dude.eaglercraft.beta.server; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.bukkit.util.config.Configuration; + +public class EaglercraftConfig { + + private Configuration yamlFile = null; + + private boolean enablePasswordLoginValue = true; + private boolean requirePasswordLoginValue = true; + private boolean allowSelfRegistrationValue = false; + private boolean allowSelfRegistrationWithoutExpireValue = false; + private boolean allowSelfChangePasswordValue = true; + private boolean allowSelfRenewPasswordValue = true; + private boolean allowSelfRenewPasswordWithTimeValue = false; + private boolean allowSelfDeletePasswordValue = false; + private int defaultPasswordExpireTimeValue = 7 * 24 * 60 * 60; + private int maximumPasswordExpireTimeValue = 21 * 24 * 60 * 60; + private boolean allowPasswordsWithoutExpireValue = true; + + private static final File configFile = new File("eagler.yml"); + + public boolean enablePasswordLogin() { + return enablePasswordLoginValue; + } + + public boolean requirePasswordLogin() { + return requirePasswordLoginValue; + } + + public boolean allowSelfRegistration() { + return allowSelfRegistrationValue; + } + + public boolean allowSelfRegistrationWithoutExpire() { + return allowSelfRegistrationWithoutExpireValue; + } + + public boolean allowSelfChangePassword() { + return allowSelfChangePasswordValue; + } + + public boolean allowSelfRenewPassword() { + return allowSelfRenewPasswordValue; + } + + public boolean allowSelfRenewPasswordWithTime() { + return allowSelfRenewPasswordWithTimeValue; + } + + public boolean allowSelfDeletePassword() { + return allowSelfDeletePasswordValue; + } + + public int defaultPasswordExpireTime() { + return defaultPasswordExpireTimeValue; + } + + public int maximumPasswordExpireTime() { + return maximumPasswordExpireTimeValue; + } + + public boolean allowPasswordsWithoutExpire() { + return allowPasswordsWithoutExpireValue; + } + + public void reload() { + if(yamlFile == null) { + if(!configFile.exists()) { + InputStream is = EaglercraftConfig.class.getResourceAsStream("/default_eagler_config.yml"); + if(is == null) { + System.err.println("The file '/default_eagler_config.yml' could not be located in this jar!"); + return; + } + try(FileOutputStream os = new FileOutputStream(configFile)) { + byte[] buffer = new byte[1024]; + int i; + while((i = is.read(buffer)) > 0) { + os.write(buffer, 0, i); + } + os.close(); + }catch(IOException ex) { + System.err.println("The default config fould not be written! (writing '" + configFile.getName() + "')"); + ex.printStackTrace(); + } + try { + is.close(); + } catch (IOException e) { + } + } + yamlFile = new Configuration(configFile); + yamlFile.load(); + enablePasswordLoginValue = yamlFile.getBoolean("enable_password_logins", true); + }else { + yamlFile.load(); + } + + if(enablePasswordLoginValue != yamlFile.getBoolean("enable_password_logins", true)) { + System.err.println("Please restart the server when you change the 'enable_password_logins' option "); + } + + requirePasswordLoginValue = yamlFile.getBoolean("only_allow_registered_users_to_login", true); + allowPasswordsWithoutExpireValue = yamlFile.getBoolean("allow_passwords_without_expiration", true); + allowSelfRegistrationValue = yamlFile.getBoolean("allow_self_registration", false); + allowSelfRegistrationWithoutExpireValue = yamlFile.getBoolean("allow_self_registration_without_expiration", false); + allowSelfChangePasswordValue = yamlFile.getBoolean("allow_self_change_password", true); + allowSelfRenewPasswordValue = yamlFile.getBoolean("allow_self_renew_password", true); + allowSelfRenewPasswordWithTimeValue = yamlFile.getBoolean("allow_self_change_password_expiration", false); + allowSelfDeletePasswordValue = yamlFile.getBoolean("allow_self_delete_password", false); + defaultPasswordExpireTimeValue = yamlFile.getInt("default_password_expire_time_seconds", 604800); // 1 week + maximumPasswordExpireTimeValue = yamlFile.getInt("maximum_password_expire_time_seconds", 1814400); // 1 week + } + +} diff --git a/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/EaglercraftServer.java b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/EaglercraftServer.java index a47fa89..d7bd427 100644 --- a/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/EaglercraftServer.java +++ b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/EaglercraftServer.java @@ -2,21 +2,34 @@ package net.lax1dude.eaglercraft.beta.server; import java.io.File; import java.io.IOException; +import java.util.regex.Pattern; import org.bukkit.Server; import org.bukkit.command.Command; import org.bukkit.command.CommandMap; import org.bukkit.command.CommandSender; import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.event.Event.Priority; +import org.bukkit.event.Event.Type; +import org.bukkit.event.Event; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.event.player.PlayerListener; +import org.bukkit.plugin.EventExecutor; +import org.bukkit.plugin.InvalidDescriptionException; +import org.bukkit.plugin.InvalidPluginException; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginLoader; import org.bukkit.plugin.PluginManager; import org.bukkit.util.config.Configuration; +import net.lax1dude.eaglercraft.beta.server.PasswordManager.PasswordEntry; import net.lax1dude.eaglercraft.beta.server.commands.*; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.PacketRegister; -public class EaglercraftServer { +public class EaglercraftServer extends PlayerListener { private static final File dataFolder = new File("."); private static CraftServer server = null; @@ -24,11 +37,33 @@ public class EaglercraftServer { private static PluginManager pluginManager = null; private static boolean passwordDBWorking = false; + private static EaglercraftServer instance = null; + + public static final EaglercraftConfig config = new EaglercraftConfig(); + private static final Plugin dummyPlugin = new Plugin() { @Override public File getDataFolder() { return dataFolder; } @Override public PluginDescriptionFile getDescription() { return new PluginDescriptionFile("EaglercraftServer", "0.0.0", ""); } @Override public Configuration getConfiguration() { return null; } - @Override public PluginLoader getPluginLoader() { return null; } + @Override public PluginLoader getPluginLoader() { return new PluginLoader() { + @Override public Plugin loadPlugin(File var1) throws InvalidPluginException, InvalidDescriptionException { + return dummyPlugin; + } + @Override public Pattern[] getPluginFileFilters() { + return new Pattern[0]; + } + @Override public EventExecutor createExecutor(Type var1, Listener var2) { + if(var1 == Type.PLAYER_JOIN) { + return new EventExecutor() { @Override public void execute(Listener var1, Event var2) { + ((PlayerListener)var1).onPlayerJoin((PlayerEvent)var2); + }}; + }else { + return null; + } + } + @Override public void enablePlugin(Plugin var1) { } + @Override public void disablePlugin(Plugin var1) { } + }; } @Override public Server getServer() { return server; } @Override public boolean isEnabled() { return true; } @Override public void onDisable() { } @@ -38,27 +73,42 @@ public class EaglercraftServer { return false; } }; + + static { + PacketRegister.register(69, Packet69EaglercraftData.class); + } public static void installHooks(CraftServer craftServer, CommandMap commandMap) { server = craftServer; commands = commandMap; pluginManager = craftServer.getPluginManager(); + instance = new EaglercraftServer(); - try { - PasswordManager.loadPasswordDB(); - passwordDBWorking = true; - } catch (IOException e) { - PasswordManager.log.severe("ERROR: could not create or load password database!"); - PasswordManager.log.severe("Eaglercraft will not function correctly, unless you disable password login"); - e.printStackTrace(); - passwordDBWorking = false; + PasswordManager.log.info("Loading Eaglercraft Bukkit..."); + + config.reload(); + + if(config.enablePasswordLogin()) { + try { + PasswordManager.loadPasswordDB(); + passwordDBWorking = true; + } catch (IOException e) { + PasswordManager.log.severe("ERROR: could not create or load password database!"); + PasswordManager.log.severe("Eaglercraft will not function correctly, unless you disable password login"); + e.printStackTrace(); + passwordDBWorking = false; + } } + + pluginManager.registerEvent(Type.PLAYER_JOIN, instance, Priority.Normal, dummyPlugin); registerCommand(new CommandSetPassword()); registerCommand(new CommandChangePassword()); registerCommand(new CommandClearPassword()); registerCommand(new CommandListPasswords()); registerCommand(new CommandPasswordExpires()); + registerCommand(new CommandRenewPassword()); + registerCommand(new CommandRegister()); } @@ -68,6 +118,30 @@ public class EaglercraftServer { private static void registerCommand(Command command) { commands.register(command.getName(), "eagler", command); + for(String s : command.getAliases()) { + commands.register(s, "eagler", command); + } + } + + @Override + public void onPlayerJoin(PlayerEvent event) { + if(config.enablePasswordLogin()) { + PasswordEntry et = PasswordManager.load(event.getPlayer().getName()); + if(et != null && et.secondsRemaining() != Integer.MAX_VALUE) { + event.getPlayer().sendMessage("Your password will expire after " + CommandListPasswords.expiresAfter(et.secondsRemaining())); + if(config.allowSelfRenewPassword()) { + event.getPlayer().sendMessage("Use /renew-password to extend"); + } + } + } + } + + public static CraftServer getMinecraftServer() { + return server; + } + + public static PluginManager getPluginManager() { + return pluginManager; } } diff --git a/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/NetEaglerServerHandler.java b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/NetEaglerServerHandler.java new file mode 100644 index 0000000..36994cb --- /dev/null +++ b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/NetEaglerServerHandler.java @@ -0,0 +1,56 @@ +package net.lax1dude.eaglercraft.beta.server; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; + +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.NetServerHandler; +import net.minecraft.server.NetworkManager; +import net.minecraft.server.Packet255KickDisconnect; + +public class NetEaglerServerHandler extends NetServerHandler { + + public final byte[] skinData; + private final MinecraftServer server; + + public NetEaglerServerHandler(MinecraftServer minecraftserver, NetworkManager networkmanager, EntityPlayer entityplayer, byte[] skinData) { + super(minecraftserver, networkmanager, entityplayer); + this.server = minecraftserver; + this.skinData = skinData; + } + + public void a(Packet69EaglercraftData pkt) { + if(pkt.type.equals("EAG|RequestPlayerSkin")) { + DataInputStream dis = new DataInputStream(new ByteArrayInputStream(pkt.data)); + try { + int cookie = dis.readUnsignedShort(); + String un = dis.readUTF(); + if(dis.available() > 0) { + throw new IOException("Packet has " + dis.available() + " extra bytes!"); + } + EntityPlayer ep = server.f.i(un); + if(ep != null) { + if(ep.a instanceof NetEaglerServerHandler) { + byte[] pk = ((NetEaglerServerHandler)ep.a).skinData; + if(pk != null) { + byte[] ret = new byte[pk.length + 2]; + ret[0] = (byte)((cookie >> 8) & 0xFF); + ret[1] = (byte)(cookie & 0xFF); + System.arraycopy(pk, 0, ret, 2, pk.length); + this.b.a(new Packet69EaglercraftData("EAG|PlayerSkin", ret)); + }else { + this.b.a(new Packet69EaglercraftData("EAG|PlayerSkin", new byte[] { (byte)((cookie >> 8) & 0xFF), (byte)(cookie & 0xFF), (byte)0, (byte)0 })); + } + } + } + }catch(IOException ex) { + this.b.a(new Packet255KickDisconnect("Invalid Skin Request")); + this.b.c(); + this.c = true; + } + } + } + +} diff --git a/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/Packet69EaglercraftData.java b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/Packet69EaglercraftData.java new file mode 100644 index 0000000..0710591 --- /dev/null +++ b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/Packet69EaglercraftData.java @@ -0,0 +1,58 @@ +package net.lax1dude.eaglercraft.beta.server; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import net.minecraft.server.NetHandler; +import net.minecraft.server.Packet; + +public class Packet69EaglercraftData extends Packet { + + public String type; + public byte[] data; + + public Packet69EaglercraftData() { + } + + public Packet69EaglercraftData(String type, byte[] data) { + if(data.length > 65535) { + throw new IllegalArgumentException("Packet69EaglercraftData may at most carry a 65535 byte payload"); + } + this.type = type; + this.data = data; + } + + @Override + public void a(DataInputStream datainputstream) { + try { + type = datainputstream.readUTF(); + data = new byte[datainputstream.readUnsignedShort()]; + datainputstream.read(data); + }catch(IOException ex) { + throw new RuntimeException("IOException was thrown while reading Packet69EaglercraftData!", ex); + } + } + + @Override + public void a(DataOutputStream var1) { + try { + var1.writeUTF(type); + var1.writeShort(data.length); + var1.write(data); + }catch(IOException ex) { + throw new RuntimeException("IOException was thrown while writing Packet69EaglercraftData!", ex); + } + } + + @Override + public void a(NetHandler var1) { + var1.a(this); + } + + @Override + public int a() { + return 2 + type.length() + 2 + data.length; + } + +} diff --git a/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/PasswordManager.java b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/PasswordManager.java index bb70e04..51014f0 100644 --- a/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/PasswordManager.java +++ b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/PasswordManager.java @@ -169,7 +169,7 @@ public class PasswordManager { continue; } synchronized(passwordEntries) { - if(pse.secondsRemaining() > 0) { + if(pse.secondsRemaining() > 0 && !(!EaglercraftServer.config.allowPasswordsWithoutExpire() && pse.expiresAfter == -1)) { passwordEntries.put(pse.username, pse); }else { mustRewrite = true; @@ -410,6 +410,10 @@ public class PasswordManager { public static void create(String username, String password, int expiresAfter) { discardExpiredPasswords(); + if(expiresAfter < -1) { + expiresAfter = -1; + } + byte[] salt = new byte[9]; synchronized(rand) { rand.nextBytes(salt); @@ -444,12 +448,43 @@ public class PasswordManager { } } + public static int changeExpires(String username, int expiresAfter) { + PasswordEntry et; + synchronized(passwordEntries) { + et = passwordEntries.get(username.toLowerCase()); + } + if(et != null) { + if(expiresAfter == -1) { + expiresAfter = et.expiresAfter; + } + synchronized(passwordEntries) { + passwordEntries.put(et.username, new PasswordEntry(et.username, et.salt, et.password, System.currentTimeMillis(), expiresAfter == -2 ? -1 : expiresAfter)); + } + } + if(discardExpiredPasswords() || et != null) { + try { + syncDatabase(); + }catch(SyncException e) { + Throwable t = e.getCause(); + System.err.println("Could not write passwords to disk!"); + if(t != null) { + t.printStackTrace(); + }else { + e.printStackTrace(); + } + } + } + return et == null ? -1 : (expiresAfter == -1 ? -2 : expiresAfter); + } + private static boolean discardExpiredPasswords() { boolean flag = false; + boolean removePasswordsWithoutExpire = !EaglercraftServer.config.allowPasswordsWithoutExpire(); synchronized(passwordEntries) { Iterator itr = passwordEntries.values().iterator(); while(itr.hasNext()) { - if(itr.next().secondsRemaining() <= 0) { + PasswordEntry et = itr.next(); + if(et.secondsRemaining() <= 0 || (removePasswordsWithoutExpire && et.expiresAfter == -1)) { flag = true; itr.remove(); } diff --git a/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandChangePassword.java b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandChangePassword.java index 154a676..8576fe6 100644 --- a/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandChangePassword.java +++ b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandChangePassword.java @@ -24,6 +24,10 @@ public class CommandChangePassword extends EaglerCommand { @Override protected void execute(CommandSender sender, String[] args) { + if(!EaglercraftServer.config.enablePasswordLogin()) { + sender.sendMessage(ChatColor.RED + "Error: password login is disabled"); + return; + } if(sender instanceof Player) { if(!EaglercraftServer.hasPasswordDB()) { sender.sendMessage(ChatColor.RED + "Error: the password database is not initialized, it probably won't save your changes"); @@ -31,8 +35,14 @@ public class CommandChangePassword extends EaglerCommand { if(args.length != 1) { throw new IncorrectUsageException("this command only takes 1 argument!"); } - + if(args[0].length() < 3) { + throw new IncorrectUsageException("A password must be at least 3 characters!"); + } if(((CraftPlayer)sender).getHandle().a.b instanceof EaglercraftWebsocketNetworkManager) { + if(!EaglercraftServer.config.allowSelfChangePassword()) { + sender.sendMessage(ChatColor.RED + "Error: you are not allowed to change your password"); + return; + } PasswordEntry et = PasswordManager.load(((Player)sender).getName()); if(et != null) { PasswordManager.create(et.username, args[0], et.expiresAfter); diff --git a/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandClearPassword.java b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandClearPassword.java index 20cc006..58cce61 100644 --- a/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandClearPassword.java +++ b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandClearPassword.java @@ -15,7 +15,7 @@ public class CommandClearPassword extends EaglerCommand { public CommandClearPassword() { super("clear-password"); - setAliases(Arrays.asList("clear-pass")); + setAliases(Arrays.asList("clear-pass", "remove-password", "remove-pass", "delete-password", "delete-pass")); setNeedsOp(false); setTooltip("Removes a password from a username, or your own username, for Eaglercraft connections"); setUsage("/clear-password [username]"); @@ -23,15 +23,28 @@ public class CommandClearPassword extends EaglerCommand { @Override protected void execute(CommandSender sender, String[] args) { + if(!EaglercraftServer.config.enablePasswordLogin()) { + sender.sendMessage(ChatColor.RED + "Error: password login is disabled"); + return; + } if(!EaglercraftServer.hasPasswordDB()) { sender.sendMessage(ChatColor.RED + "Error: the password database is not initialized, it probably won't save your changes"); } if(sender instanceof CraftPlayer && ((CraftPlayer)sender).getHandle().a.b instanceof EaglercraftWebsocketNetworkManager && (args.length == 0 || (args.length == 1 && args[0].equalsIgnoreCase(((Player)sender).getName())))) { - if(PasswordManager.delete(((Player)sender).getName())) { - sender.sendMessage("Your password was removed."); + if(EaglercraftServer.config.allowSelfDeletePassword() || EaglercraftServer.config.allowSelfRegistration()) { + if(PasswordManager.delete(((Player)sender).getName())) { + if(EaglercraftServer.config.requirePasswordLogin()) { + ((Player)sender).kickPlayer("Your password was removed."); + }else { + sender.sendMessage("Your password was removed."); + } + }else { + sender.sendMessage(ChatColor.RED + "You do not have a password on this account!"); + } }else { - sender.sendMessage(ChatColor.RED + "You do not have a password on this account!"); + sender.sendMessage(ChatColor.RED + "You cannot remove the password from your username."); } + return; }else if(args.length != 1) { throw new IncorrectUsageException("this command only takes 1 argument!"); } diff --git a/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandListPasswords.java b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandListPasswords.java index 33e0e59..c754e97 100644 --- a/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandListPasswords.java +++ b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandListPasswords.java @@ -22,6 +22,10 @@ public class CommandListPasswords extends EaglerCommand { @Override protected void execute(CommandSender sender, String[] args) { + if(!EaglercraftServer.config.enablePasswordLogin()) { + sender.sendMessage(ChatColor.RED + "Error: password login is disabled"); + return; + } if(args.length != 0) { throw new IncorrectUsageException("This command does not take any arguments"); } @@ -36,24 +40,27 @@ public class CommandListPasswords extends EaglerCommand { int characterWidth = (sender instanceof Player) ? 60 : 40; String row = ""; for(PasswordEntry s : cc) { - String rowAdd = s.username + (s.expiresAfter <= 0 ? " (*)" : " (" + expiresAfter(s.expiresAfter) + ")"); + String rowAdd = s.username + (s.expiresAfter <= 0 ? " (*)" : " (" + expiresAfter(s.secondsRemaining()) + ")"); if(row.length() + rowAdd.length() + 2 > characterWidth) { - sender.sendMessage(" " + row); + sender.sendMessage(" " + row); row = ""; } if(row.length() > 0) { - row = ", " + rowAdd; + row = row + ", " + rowAdd; }else { row = rowAdd; } } if(row.length() > 0) { - sender.sendMessage(" " + row); + sender.sendMessage(" " + row); } } } public static String expiresAfter(int remaining) { + if(remaining < 0) { + return "never"; + } if(remaining < 60) { return remaining + "s"; }else if(remaining < 60 * 60) { diff --git a/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandPasswordExpires.java b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandPasswordExpires.java index 6aa7292..ee68246 100644 --- a/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandPasswordExpires.java +++ b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandPasswordExpires.java @@ -24,6 +24,10 @@ public class CommandPasswordExpires extends EaglerCommand { @Override protected void execute(CommandSender sender, String[] args) { + if(!EaglercraftServer.config.enablePasswordLogin()) { + sender.sendMessage(ChatColor.RED + "Error: password login is disabled"); + return; + } if(!EaglercraftServer.hasPasswordDB()) { sender.sendMessage(ChatColor.RED + "Error: the password database is not initialized, it probably won't save your changes"); } diff --git a/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandRegister.java b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandRegister.java new file mode 100644 index 0000000..ed2a497 --- /dev/null +++ b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandRegister.java @@ -0,0 +1,70 @@ +package net.lax1dude.eaglercraft.beta.server.commands; + +import java.util.Arrays; + +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.entity.Player; + +import net.lax1dude.eaglercraft.beta.server.EaglercraftServer; +import net.lax1dude.eaglercraft.beta.server.PasswordManager; +import net.minecraft.server.EaglercraftWebsocketNetworkManager; + +public class CommandRegister extends EaglerCommand { + + public CommandRegister() { + super("register-password"); + setAliases(Arrays.asList("register-pass", "password-register", "pass-register", "eagler-register", "eag-register")); + setNeedsOp(false); + setTooltip("Register a password for your username"); + setUsage("/register-password [expires after][s|m|h|d|w]"); + } + + @Override + protected void execute(CommandSender sender, String[] args) { + if(!EaglercraftServer.config.enablePasswordLogin()) { + sender.sendMessage(ChatColor.RED + "Error: password login is disabled"); + return; + } + if(!EaglercraftServer.hasPasswordDB()) { + sender.sendMessage(ChatColor.RED + "Error: the password database is not initialized, it probably won't save your changes"); + } + if(sender instanceof Player && ((CraftPlayer)sender).getHandle().a.b instanceof EaglercraftWebsocketNetworkManager) { + if(!EaglercraftServer.config.allowSelfRegistration()) { + sender.sendMessage(ChatColor.RED + "Error: password registration is disabled"); + return; + } + if(PasswordManager.load(((Player)sender).getName()) != null) { + sender.sendMessage(ChatColor.RED + "Error: you are already registered on this server, use /change-password to edit it"); + }else { + if(args.length != 1 && args.length != 2) { + throw new IncorrectUsageException("this command takes 1 or 2 arguments!"); + } + if(args[0].length() < 3) { + throw new IncorrectUsageException("A password must be at least 3 characters!"); + } + int expires = EaglercraftServer.config.defaultPasswordExpireTime(); + if(args.length == 2) { + expires = CommandRenewPassword.tryParseTime(args[1]); + if(expires == -1) { + throw new IncorrectUsageException("Expires time is invalid!"); + } + } + if(expires == -2 && !(EaglercraftServer.config.allowSelfRegistrationWithoutExpire() || !EaglercraftServer.config.allowPasswordsWithoutExpire())) { + sender.sendMessage(ChatColor.RED + "Error: you cannot register a password"); + return; + } + if(expires > EaglercraftServer.config.maximumPasswordExpireTime()) { + sender.sendMessage(ChatColor.RED + "Error: the maximum time before your password expires can be at most " + CommandListPasswords.expiresAfter(expires)); + return; + } + PasswordManager.create(((Player)sender).getName(), args[0], expires == -2 ? -1 : expires); + sender.sendMessage("Your password was registered." + (expires == -2 ? "" : " It will expire in " + CommandListPasswords.expiresAfter(expires))); + } + }else { + sender.sendMessage(ChatColor.RED + "Error: only players connected via websocket can use this command"); + } + } + +} diff --git a/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandRenewPassword.java b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandRenewPassword.java index 1bd46a9..e52db61 100644 --- a/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandRenewPassword.java +++ b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandRenewPassword.java @@ -9,6 +9,7 @@ import org.bukkit.entity.Player; import net.lax1dude.eaglercraft.beta.server.EaglercraftServer; import net.lax1dude.eaglercraft.beta.server.PasswordManager; +import net.lax1dude.eaglercraft.beta.server.PasswordManager.PasswordEntry; import net.minecraft.server.EaglercraftWebsocketNetworkManager; public class CommandRenewPassword extends EaglerCommand { @@ -23,31 +24,166 @@ public class CommandRenewPassword extends EaglerCommand { @Override protected void execute(CommandSender sender, String[] args) { + if(!EaglercraftServer.config.enablePasswordLogin()) { + sender.sendMessage(ChatColor.RED + "Error: password login is disabled"); + return; + } - // Tomorrow: program this command + boolean isPlayer = sender instanceof Player; + boolean isWsPlayer = isPlayer && ((CraftPlayer)sender).getHandle().a.b instanceof EaglercraftWebsocketNetworkManager; + boolean isSelfPlayer = isPlayer && args.length >= 1 && args[0].equalsIgnoreCase(((Player)sender).getName()); + int is1stTime = args.length >= 1 ? tryParseTime(args[0]) : -1; + int is2stTime = args.length >= 2 ? tryParseTime(args[1]) : -1; + int mpe = EaglercraftServer.config.maximumPasswordExpireTime(); + if(is1stTime > mpe || is2stTime > mpe) { + sender.sendMessage(ChatColor.RED + "Error: the maximum time before a password expires can be at most " + CommandListPasswords.expiresAfter(mpe)); + return; + } + + if(!EaglercraftServer.config.allowPasswordsWithoutExpire() && (is1stTime == -2 || is2stTime == -2)) { + sender.sendMessage(ChatColor.RED + "Error: passwords that do not expire are not enabled on this server!"); + return; + } if(!EaglercraftServer.hasPasswordDB()) { sender.sendMessage(ChatColor.RED + "Error: the password database is not initialized, it probably won't save your changes"); } - if(sender instanceof CraftPlayer && ((CraftPlayer)sender).getHandle().a.b instanceof EaglercraftWebsocketNetworkManager && (args.length == 0 || (args.length == 1 && args[0].equalsIgnoreCase(((Player)sender).getName())))) { - if(PasswordManager.delete(((Player)sender).getName())) { - sender.sendMessage("Your password was removed."); + + if(args.length == 0 || (args.length == 1 && isSelfPlayer)) { + if(isPlayer && (isWsPlayer || sender.isOp())) { + if(!sender.isOp() && !EaglercraftServer.config.allowSelfRenewPassword()) { + sender.sendMessage(ChatColor.RED + "Error: you cannot renew your password on this server"); + return; + } + int n = PasswordManager.changeExpires(((Player)sender).getName(), -1); + if(n > 0) { + sender.sendMessage("Your password will expire in " + CommandListPasswords.expiresAfter(n) + "."); + }else { + if(n == -2) { + sender.sendMessage("Your password is not going to expire."); + }else { + sender.sendMessage(ChatColor.RED + "Error: you do not have a password to renew"); + } + } }else { - sender.sendMessage(ChatColor.RED + "You do not have a password on this account!"); + sender.sendMessage(ChatColor.RED + "Error: you need to be logged in via websocket to use this command"); } - }else if(args.length != 1) { - throw new IncorrectUsageException("this command only takes 1 argument!"); - } - if(sender.isOp()) { - if(PasswordManager.delete(args[0])) { - sender.sendMessage("Password for '" + args[0] + "' was removed"); + }else if(args.length == 1 && is1stTime != -1) { + if(isPlayer && (isWsPlayer || sender.isOp())) { + if(!sender.isOp() && !EaglercraftServer.config.allowSelfRenewPassword()) { + sender.sendMessage(ChatColor.RED + "Error: you cannot renew your password on this server"); + return; + } + System.out.println(EaglercraftServer.config.allowSelfRenewPasswordWithTime()); + if(!sender.isOp() && !EaglercraftServer.config.allowSelfRenewPasswordWithTime()) { + sender.sendMessage(ChatColor.RED + "Error: you cannot manually set the time until your password expires on this server"); + return; + } + PasswordEntry pe = PasswordManager.load(((Player)sender).getName()); + if(pe.expiresAfter == -1) { + sender.sendMessage("Your password is not going to expire."); + }else { + if(!sender.isOp() && is1stTime == -2) { + sender.sendMessage(ChatColor.RED + "Error: you cannot renew your password not to expire"); + return; + }else { + int n = PasswordManager.changeExpires(((Player)sender).getName(), is1stTime); + if(n > 0) { + sender.sendMessage("Your password will expire in " + CommandListPasswords.expiresAfter(n) + "."); + }else { + if(n == -2) { + sender.sendMessage("Your password is not going to expire."); + }else { + sender.sendMessage(ChatColor.RED + "Error: you do not have a password to renew"); + } + } + } + } }else { - sender.sendMessage(ChatColor.RED + "The user '" + args[0] + "' does not have a password!"); + sender.sendMessage(ChatColor.RED + "Error: you need to be logged in via websocket to use this command"); + } + }else if(args.length == 2 && isSelfPlayer && is2stTime != -1) { + if(isPlayer && (isWsPlayer || sender.isOp())) { + if(!sender.isOp() && !EaglercraftServer.config.allowSelfRenewPassword()) { + sender.sendMessage(ChatColor.RED + "Error: you cannot renew your password on this server"); + return; + } + if(!sender.isOp() && !EaglercraftServer.config.allowSelfRenewPasswordWithTime()) { + sender.sendMessage(ChatColor.RED + "Error: you cannot manually set the time until your password expires on this server"); + return; + } + PasswordEntry pe = PasswordManager.load(((Player)sender).getName()); + if(pe.expiresAfter == -1) { + sender.sendMessage("Your password is not going to expire."); + }else { + if(!sender.isOp() && is2stTime == -2) { + sender.sendMessage(ChatColor.RED + "Error: you cannot renew your password not to expire"); + return; + }else { + int n = PasswordManager.changeExpires(((Player)sender).getName(), is2stTime); + if(n > 0) { + sender.sendMessage("Your password will expire in " + CommandListPasswords.expiresAfter(n) + "."); + }else { + if(n == -2) { + sender.sendMessage("Your password is not going to expire."); + }else { + sender.sendMessage(ChatColor.RED + "Error: you do not have a password to renew"); + } + } + } + } + }else { + sender.sendMessage(ChatColor.RED + "Error: you need to be logged in via websocket to use this command"); + } + }else if(args.length == 2 && is2stTime != -1) { + if(sender.isOp()) { + int n = PasswordManager.changeExpires(args[0], is2stTime); + if(n > 0) { + sender.sendMessage("The password for '" + args[0] + "' will expire in " + CommandListPasswords.expiresAfter(n) + "."); + }else { + if(n == -2) { + sender.sendMessage("The password for '" + args[0] + "' will not expire."); + }else { + sender.sendMessage(ChatColor.RED + "Error: this player '" + args[0] + "' does not have a password to renew"); + } + } + }else { + sender.sendMessage(ChatColor.RED + "Error: you need /op to use this command!"); } }else { - sender.sendMessage(ChatColor.RED + "Error: you need /op to use this command!"); + throw new IncorrectUsageException("Illegal argument combination"); } } - + + public static int tryParseTime(String str) { + int expires = -1; + if(str.length() >= 2) { + if(str.equalsIgnoreCase("never") || str.equalsIgnoreCase("infinite") || str.equalsIgnoreCase("infinity")) { + return -2; + } + int mul = 60 * 60 * 24; + String exp = str.toLowerCase(); + if(exp.endsWith("s")) { + mul = 1; + exp = exp.substring(0, exp.length() - 1); + }else if(exp.endsWith("m")) { + mul = 60; + exp = exp.substring(0, exp.length() - 1); + }else if(exp.endsWith("h")) { + mul = 60 * 60; + exp = exp.substring(0, exp.length() - 1); + }else if(exp.endsWith("d")) { + exp = exp.substring(0, exp.length() - 1); + }else if(exp.endsWith("w")) { + mul = 60 * 60 * 24 * 7; + exp = exp.substring(0, exp.length() - 1); + } + try { + expires = Integer.parseInt(exp) * mul; + }catch(NumberFormatException ex) { + } + } + return expires < 0 ? -1 : expires; + } } diff --git a/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandSetPassword.java b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandSetPassword.java index 745e145..4ce7c32 100644 --- a/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandSetPassword.java +++ b/bukkit/src/main/java/net/lax1dude/eaglercraft/beta/server/commands/CommandSetPassword.java @@ -19,6 +19,10 @@ public class CommandSetPassword extends EaglerCommand { @Override protected void execute(CommandSender sender, String[] args) { + if(!EaglercraftServer.config.enablePasswordLogin()) { + sender.sendMessage(ChatColor.RED + "Error: password login is disabled"); + return; + } if(!EaglercraftServer.hasPasswordDB()) { sender.sendMessage(ChatColor.RED + "Error: the password database is not initialized, it probably won't save your changes"); } @@ -28,34 +32,27 @@ public class CommandSetPassword extends EaglerCommand { if(args[0].length() > 16) { throw new IncorrectUsageException("the maximum length for a username is 16 characters!"); } - int expires = -1; + if(args[1].length() < 3) { + throw new IncorrectUsageException("A password must be at least 3 characters!"); + } + int expires = EaglercraftServer.config.defaultPasswordExpireTime(); if(args.length == 3) { - int mul = 60 * 60 * 24; - String exp = args[2].toLowerCase(); - if(exp.endsWith("s")) { - mul = 1; - exp = exp.substring(0, exp.length() - 1); - }else if(exp.endsWith("m")) { - mul = 60; - exp = exp.substring(0, exp.length() - 1); - }else if(exp.endsWith("h")) { - mul = 60 * 60; - exp = exp.substring(0, exp.length() - 1); - }else if(exp.endsWith("d")) { - exp = exp.substring(0, exp.length() - 1); - }else if(exp.endsWith("w")) { - mul = 60 * 60 * 24 * 7; - exp = exp.substring(0, exp.length() - 1); + expires = CommandRenewPassword.tryParseTime(args[2]); + if(expires == -1) { + throw new IncorrectUsageException("Expires time is invalid!"); } - try { - expires = Integer.parseInt(exp) * mul; - }catch(NumberFormatException ex) { - throw new IncorrectUsageException("The number '" + exp + "' is invalid!"); - } - if(expires < 1) { - throw new IncorrectUsageException("Expires time must be positive!"); + if(expires == -2) { + expires = -1; } } + if(expires > EaglercraftServer.config.maximumPasswordExpireTime()) { + sender.sendMessage(ChatColor.RED + "Error: the maximum time before a password expires can be at most " + CommandListPasswords.expiresAfter(EaglercraftServer.config.maximumPasswordExpireTime())); + return; + } + if(expires == -1 && !EaglercraftServer.config.allowPasswordsWithoutExpire()) { + sender.sendMessage(ChatColor.RED + "Error: passwords that do not expire are disabled!"); + return; + } PasswordManager.create(args[0], args[1], expires); sender.sendMessage("Password for '" + args[0] + "' was changed"); } diff --git a/bukkit/src/main/java/net/minecraft/server/EaglercraftVanillaNetworkManager.java b/bukkit/src/main/java/net/minecraft/server/EaglercraftVanillaNetworkManager.java index dcff6f3..4a03be9 100644 --- a/bukkit/src/main/java/net/minecraft/server/EaglercraftVanillaNetworkManager.java +++ b/bukkit/src/main/java/net/minecraft/server/EaglercraftVanillaNetworkManager.java @@ -9,7 +9,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -public class EaglercraftVanillaNetworkManager implements NetworkManager { +public class EaglercraftVanillaNetworkManager extends NetworkManager { public static final Object a = new Object(); public static int b; public static int c; diff --git a/bukkit/src/main/java/net/minecraft/server/EaglercraftWebsocketListenerThread.java b/bukkit/src/main/java/net/minecraft/server/EaglercraftWebsocketListenerThread.java index e56bc90..a302fea 100644 --- a/bukkit/src/main/java/net/minecraft/server/EaglercraftWebsocketListenerThread.java +++ b/bukkit/src/main/java/net/minecraft/server/EaglercraftWebsocketListenerThread.java @@ -29,7 +29,10 @@ public class EaglercraftWebsocketListenerThread extends WebSocketServer { @Override public void onClose(WebSocket arg0, int arg1, String arg2, boolean arg3) { - // rip + EaglercraftWebsocketNetworkManager mgr = arg0.getAttachment(); + if(mgr != null && !mgr.disconnected) { + mgr.a("disconnect.close"); + } } @Override diff --git a/bukkit/src/main/java/net/minecraft/server/EaglercraftWebsocketNetworkManager.java b/bukkit/src/main/java/net/minecraft/server/EaglercraftWebsocketNetworkManager.java index c012f71..84ab508 100644 --- a/bukkit/src/main/java/net/minecraft/server/EaglercraftWebsocketNetworkManager.java +++ b/bukkit/src/main/java/net/minecraft/server/EaglercraftWebsocketNetworkManager.java @@ -11,31 +11,23 @@ import java.util.List; import org.java_websocket.WebSocket; -import net.minecraft.server.NetHandler; -import net.minecraft.server.NetServerHandler; -import net.minecraft.server.Packet; - -public class EaglercraftWebsocketNetworkManager implements NetworkManager { +public class EaglercraftWebsocketNetworkManager extends NetworkManager { public static final int PACKET_LIMIT = 300; public static final int PACKET_PER_SECOND_QUOTA = 1000 / 35; - public static final int PACKET_MAX_SIZE = 1536; + public static final int PACKET_MAX_SIZE = 9600; final WebSocket websocket; NetHandler netHandler; - - // Next step: add cooldown to implement 'delayedPackets' private volatile int packetCounter = 0; private long packetDecrement; private int timeoutCounter = 0; - private final List delayedPackets = new LinkedList(); private final List readPackets = new LinkedList(); protected final List writePackets = new LinkedList(); - private boolean delay = false; - private int delayTimer = 0; + public boolean disconnected = false; private final Thread writeThread; protected final Object writeThreadLock = new Object(); @@ -108,6 +100,10 @@ public class EaglercraftWebsocketNetworkManager implements NetworkManager { manager.websocket.send(ByteBuffer.wrap(pktBytes)); } } + if(manager.disconnected) { + manager.websocket.close(); + break main_loop; + } }catch(Throwable t) { t.printStackTrace(); manager.a("disconnect.closed", "Packet write fault"); @@ -131,18 +127,11 @@ public class EaglercraftWebsocketNetworkManager implements NetworkManager { */ @Override public void a(Packet var1) { - if (var1.k) { - synchronized(delayedPackets) { - delayedPackets.add(var1); - } - }else { - delay = true; - synchronized(writePackets) { - writePackets.add(var1); - } - synchronized(writeThreadLock) { - writeThreadLock.notify(); - } + synchronized(writePackets) { + writePackets.add(var1); + } + synchronized(writeThreadLock) { + writeThreadLock.notify(); } } @@ -151,10 +140,8 @@ public class EaglercraftWebsocketNetworkManager implements NetworkManager { */ @Override public void a(String var1, Object... var2) { - if(!websocket.isClosed()) { - netHandler.a(var1, var2); - websocket.close(); - } + netHandler.a(var1, var2); + disconnected = true; } /** @@ -175,7 +162,7 @@ public class EaglercraftWebsocketNetworkManager implements NetworkManager { } long t = System.currentTimeMillis(); - int decr = (int) ((packetDecrement - t) / PACKET_PER_SECOND_QUOTA); + int decr = (int) ((t - packetDecrement) / PACKET_PER_SECOND_QUOTA); packetCounter -= decr; packetDecrement += decr * PACKET_PER_SECOND_QUOTA; @@ -205,20 +192,6 @@ public class EaglercraftWebsocketNetworkManager implements NetworkManager { p.a(netHandler); } } - - synchronized(delayedPackets) { - if(!delayedPackets.isEmpty() && (!delay || --delayTimer <= 0)) { - synchronized(writePackets) { - writePackets.add(delayedPackets.remove(0)); - } - synchronized(writeThreadLock) { - writeThreadLock.notify(); - } - delayTimer = 50; - } - } - - delay = false; } /** @@ -242,9 +215,7 @@ public class EaglercraftWebsocketNetworkManager implements NetworkManager { */ @Override public int d() { - synchronized(delayedPackets) { - return delayedPackets.size(); - } + return 0; } @Override diff --git a/bukkit/src/main/java/net/minecraft/server/NetHandler.java b/bukkit/src/main/java/net/minecraft/server/NetHandler.java new file mode 100644 index 0000000..a131141 --- /dev/null +++ b/bukkit/src/main/java/net/minecraft/server/NetHandler.java @@ -0,0 +1,192 @@ +package net.minecraft.server; + +import net.lax1dude.eaglercraft.beta.server.Packet69EaglercraftData; + +public class NetHandler { + public void a(Packet51MapChunk var1) { + } + + public void a(Packet var1) { + } + + public void a(String var1, Object[] var2) { + } + + public void a(Packet255KickDisconnect var1) { + this.a((Packet) var1); + } + + public void a(Packet1Login var1) { + this.a((Packet) var1); + } + + public void a(Packet10Flying var1) { + this.a((Packet) var1); + } + + public void a(Packet52MultiBlockChange var1) { + this.a((Packet) var1); + } + + public void a(Packet14BlockDig var1) { + this.a((Packet) var1); + } + + public void a(Packet53BlockChange var1) { + this.a((Packet) var1); + } + + public void a(Packet50PreChunk var1) { + this.a((Packet) var1); + } + + public void a(Packet20NamedEntitySpawn var1) { + this.a((Packet) var1); + } + + public void a(Packet30Entity var1) { + this.a((Packet) var1); + } + + public void a(Packet34EntityTeleport var1) { + this.a((Packet) var1); + } + + public void a(Packet15Place var1) { + this.a((Packet) var1); + } + + public void a(Packet16BlockItemSwitch var1) { + this.a((Packet) var1); + } + + public void a(Packet29DestroyEntity var1) { + this.a((Packet) var1); + } + + public void a(Packet21PickupSpawn var1) { + this.a((Packet) var1); + } + + public void a(Packet22Collect var1) { + this.a((Packet) var1); + } + + public void a(Packet3Chat var1) { + this.a((Packet) var1); + } + + public void a(Packet23VehicleSpawn var1) { + this.a((Packet) var1); + } + + public void a(Packet18ArmAnimation var1) { + this.a((Packet) var1); + } + + public void a(Packet19EntityAction var1) { + this.a((Packet) var1); + } + + public void a(Packet2Handshake var1) { + this.a((Packet) var1); + } + + public void a(Packet24MobSpawn var1) { + this.a((Packet) var1); + } + + public void a(Packet4UpdateTime var1) { + this.a((Packet) var1); + } + + public void a(Packet6SpawnPosition var1) { + this.a((Packet) var1); + } + + public void a(Packet28EntityVelocity var1) { + this.a((Packet) var1); + } + + public void a(Packet40EntityMetadata var1) { + this.a((Packet) var1); + } + + public void a(Packet39AttachEntity var1) { + this.a((Packet) var1); + } + + public void a(Packet7UseEntity var1) { + this.a((Packet) var1); + } + + public void a(Packet38EntityStatus var1) { + this.a((Packet) var1); + } + + public void a(Packet8UpdateHealth var1) { + this.a((Packet) var1); + } + + public void a(Packet9Respawn var1) { + this.a((Packet) var1); + } + + public void a(Packet60Explosion var1) { + this.a((Packet) var1); + } + + public void a(Packet100OpenWindow var1) { + this.a((Packet) var1); + } + + public void a(Packet101CloseWindow var1) { + this.a((Packet) var1); + } + + public void a(Packet102WindowClick var1) { + this.a((Packet) var1); + } + + public void a(Packet103SetSlot var1) { + this.a((Packet) var1); + } + + public void a(Packet104WindowItems var1) { + this.a((Packet) var1); + } + + public void a(Packet130UpdateSign var1) { + this.a((Packet) var1); + } + + public void a(Packet105CraftProgressBar var1) { + this.a((Packet) var1); + } + + public void a(Packet5EntityEquipment var1) { + this.a((Packet) var1); + } + + public void a(Packet106Transaction var1) { + this.a((Packet) var1); + } + + public void a(Packet25EntityPainting var1) { + this.a((Packet) var1); + } + + public void a(Packet54PlayNoteBlock var1) { + this.a((Packet) var1); + } + + public void a(Packet69EaglercraftData var1) { + this.a((Packet) var1); + } + + public void a(Packet17 var1) { + } + + public void a(Packet27 var1) { + } +} \ No newline at end of file diff --git a/bukkit/src/main/java/net/minecraft/server/NetLoginHandler.java b/bukkit/src/main/java/net/minecraft/server/NetLoginHandler.java index 76b280d..c262f34 100644 --- a/bukkit/src/main/java/net/minecraft/server/NetLoginHandler.java +++ b/bukkit/src/main/java/net/minecraft/server/NetLoginHandler.java @@ -9,6 +9,9 @@ import java.util.logging.Logger; import org.java_websocket.WebSocket; import net.lax1dude.eaglercraft.beta.server.Base64; +import net.lax1dude.eaglercraft.beta.server.EaglercraftServer; +import net.lax1dude.eaglercraft.beta.server.NetEaglerServerHandler; +import net.lax1dude.eaglercraft.beta.server.Packet69EaglercraftData; import net.lax1dude.eaglercraft.beta.server.PasswordManager; import net.lax1dude.eaglercraft.beta.server.PasswordManager.PasswordEntry; import net.lax1dude.eaglercraft.beta.server.SHA1Digest; @@ -27,6 +30,7 @@ public class NetLoginHandler extends NetHandler { private String wsUsername = null; private PasswordEntry passEntry = null; private byte[] sentSalt = null; + private byte[] skinData = null; private boolean isWebsocket() { return b instanceof EaglercraftWebsocketNetworkManager; @@ -76,19 +80,34 @@ public class NetLoginHandler extends NetHandler { public void a(Packet2Handshake packet2handshake) { if(isWebsocket()) { wsUsername = packet2handshake.a; - passEntry = PasswordManager.load(wsUsername); - if(passEntry == null) { - this.a("You're not registered on this server!"); + if(!validateUsername(wsUsername)) { + this.a("Invalid username!"); return; } - sentSalt = new byte[9]; - synchronized(PasswordManager.rand) { - PasswordManager.rand.nextBytes(sentSalt); + if(EaglercraftServer.config.enablePasswordLogin()) { + passEntry = PasswordManager.load(wsUsername); + if(passEntry == null) { + if(EaglercraftServer.config.requirePasswordLogin()) { + this.a("You're not registered on this server!"); + return; + }else { + sentSalt = new byte[0]; + this.b.a(new Packet2Handshake("NULL")); + } + }else { + sentSalt = new byte[9]; + synchronized(PasswordManager.rand) { + PasswordManager.rand.nextBytes(sentSalt); + } + byte[] sendHash = new byte[18]; + System.arraycopy(passEntry.salt, 0, sendHash, 0, 9); + System.arraycopy(sentSalt, 0, sendHash, 9, 9); + this.b.a(new Packet2Handshake(Base64.encodeBase64String(sendHash))); + } + }else { + sentSalt = new byte[0]; + this.b.a(new Packet2Handshake("NULL")); } - byte[] sendHash = new byte[18]; - System.arraycopy(passEntry.salt, 0, sendHash, 0, 9); - System.arraycopy(sentSalt, 0, sendHash, 9, 9); - this.b.a(new Packet2Handshake(Base64.encodeBase64String(sendHash))); }else { if (this.e.l) { this.i = Long.toHexString(d.nextLong()); @@ -99,6 +118,16 @@ public class NetLoginHandler extends NetHandler { } } + public static boolean validateUsername(String wsUsername2) { + if (wsUsername2.length() < 3 || wsUsername2.length() > 16) { + return false; + }else if(!wsUsername2.equals(wsUsername2.replaceAll("[^A-Za-z0-9\\-_]", "_").trim())) { + return false; + }else { + return true; + } + } + public void a(Packet1Login packet1login) { if (packet1login.a != 9) { if (packet1login.a > 9) { @@ -109,27 +138,39 @@ public class NetLoginHandler extends NetHandler { return; }else { if(isWebsocket()) { - if(wsUsername == null || !wsUsername.equals(packet1login.b)) { + if(wsUsername == null || sentSalt == null || !wsUsername.equals(packet1login.b)) { this.a("Invalid login!"); }else { this.g = packet1login.b; - SHA1Digest dg = new SHA1Digest(); - dg.update(PasswordManager.eaglerSalt, 0, PasswordManager.eaglerSalt.length); - dg.update(sentSalt, 0, 9); - dg.update(passEntry.password, 0, passEntry.password.length); - byte[] o = new byte[20]; - dg.doFinal(o, 0); - - byte[] hsh = Base64.decodeBase64(packet1login.c.replace('-', '+').replace('_', '/')); - if(Arrays.equals(o, hsh)) { - packet1login.c = "-"; - this.h = packet1login; - this.hf = f; + if(packet1login.c.equalsIgnoreCase("NULL")) { + if(sentSalt.length == 0) { + this.h = packet1login; + }else { + this.a(EaglercraftServer.config.requirePasswordLogin() ? "A password is required to join this server!" : "This username requires a password to join!"); + } }else { - this.a("Wrong password!"); + SHA1Digest dg = new SHA1Digest(); + dg.update(PasswordManager.eaglerSalt, 0, PasswordManager.eaglerSalt.length); + dg.update(sentSalt, 0, 9); + dg.update(passEntry.password, 0, passEntry.password.length); + byte[] o = new byte[20]; + dg.doFinal(o, 0); + + byte[] hsh = Base64.decodeBase64(packet1login.c.replace('-', '+').replace('_', '/')); + if(Arrays.equals(o, hsh)) { + packet1login.c = "-"; + this.h = packet1login; + this.hf = f; + }else { + this.a("Wrong password!"); + } } } }else { + if(!validateUsername(packet1login.b)) { + this.a("Invalid username!"); + return; + } this.g = packet1login.b; if (!this.e.l) { this.b(packet1login); @@ -139,12 +180,45 @@ public class NetLoginHandler extends NetHandler { } } } + + public static final int SKIN_DATA_SIZE = 64*32*4; + public void a(Packet69EaglercraftData pkt) { + if(isWebsocket()) { + if(pkt.type.equals("EAG|MySkin")) { + String inv = "Invalid skin"; + if(pkt.data.length < 2) { + this.a(inv); + }else { + int type = (int)pkt.data[0] & 0xFF; + if(type == 0) { + if(pkt.data.length == 2) { + skinData = pkt.data; + }else { + this.a(inv); + } + }else if(type == 1) { + if(pkt.data.length == SKIN_DATA_SIZE + 1) { + skinData = pkt.data; + }else { + this.a(inv); + } + }else { + this.a(inv); + } + } + } + }else { + a((Packet)pkt); + } + } + public void b(Packet1Login packet1login) { EntityPlayer entityplayer = this.e.f.a(this, packet1login.b, packet1login.c); if (entityplayer != null) { a.info(this.b() + " logged in with entity id " + entityplayer.id); - NetServerHandler netserverhandler = new NetServerHandler(this.e, this.b, entityplayer); + NetServerHandler netserverhandler = isWebsocket() ? new NetEaglerServerHandler(this.e, this.b, entityplayer, skinData) : + new NetServerHandler(this.e, this.b, entityplayer); ChunkCoordinates chunkcoordinates = entityplayer.world.l(); netserverhandler.b( new Packet1Login("", "", entityplayer.id, entityplayer.world.j(), (byte) entityplayer.world.m.g)); @@ -162,7 +236,7 @@ public class NetLoginHandler extends NetHandler { } public void a(String s, Object[] aobject) { - a.info(this.b() + " lost connection"); + a.info(this.b() + " was kicked while logging in: " + s); this.c = true; } diff --git a/bukkit/src/main/java/net/minecraft/server/NetworkManager.java b/bukkit/src/main/java/net/minecraft/server/NetworkManager.java index ac1a439..98050c2 100644 --- a/bukkit/src/main/java/net/minecraft/server/NetworkManager.java +++ b/bukkit/src/main/java/net/minecraft/server/NetworkManager.java @@ -2,45 +2,45 @@ package net.minecraft.server; import java.net.SocketAddress; -public interface NetworkManager { +public abstract class NetworkManager { /** * Set the NetHandler */ - void a(NetHandler var1); + abstract void a(NetHandler var1); /** * addToSendQueue */ - void a(Packet var1); + public abstract void a(Packet var1); /** * disconnect */ - void a(String var1, Object... var2); + abstract void a(String var1, Object... var2); /** * processReadPackets */ - void a(); + abstract void a(); /** * gets the remote address */ - SocketAddress b(); + abstract SocketAddress b(); /** * shuts connection down */ - void c(); + public abstract void c(); /** * gets a number of delayed packets */ - int d(); + abstract int d(); /** * gets if the connection is closed */ - boolean isDead(); + abstract boolean isDead(); } diff --git a/bukkit/src/main/java/net/minecraft/server/PacketRegister.java b/bukkit/src/main/java/net/minecraft/server/PacketRegister.java new file mode 100644 index 0000000..68a86ba --- /dev/null +++ b/bukkit/src/main/java/net/minecraft/server/PacketRegister.java @@ -0,0 +1,9 @@ +package net.minecraft.server; + +public class PacketRegister { + + public static void register(int id, Class pkt) { + Packet.a(id, pkt); + } + +} diff --git a/bukkit/src/main/java/net/minecraft/server/PlayerInstance.java b/bukkit/src/main/java/net/minecraft/server/PlayerInstance.java new file mode 100644 index 0000000..e30cb66 --- /dev/null +++ b/bukkit/src/main/java/net/minecraft/server/PlayerInstance.java @@ -0,0 +1,184 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.List; + +class PlayerInstance { + private List b; + private int c; + private int d; + private ChunkCoordIntPair e; + private short[] f; + private int g; + private int h; + private int i; + private int j; + private int k; + private int l; + private int m; + final PlayerManager a; + + public PlayerInstance(PlayerManager playermanager, int i, int j) { + this.a = playermanager; + this.b = new ArrayList(); + this.f = new short[10]; + this.g = 0; + this.c = i; + this.d = j; + this.e = new ChunkCoordIntPair(i, j); + playermanager.world.u.d(i, j); + } + + public void a(EntityPlayer entityplayer) { + if (this.b.contains(entityplayer)) { + //throw new IllegalStateException( + // "Failed to add player. " + entityplayer + " already is in chunk " + this.c + ", " + this.d); + } else { + entityplayer.g.add(this.e); + entityplayer.a.b(new Packet50PreChunk(this.e.a, this.e.b, true)); + this.b.add(entityplayer); + entityplayer.f.add(this.e); + } + } + + public void b(EntityPlayer entityplayer) { + if (!this.b.contains(entityplayer)) { + //(new IllegalStateException( + // "Failed to remove player. " + entityplayer + " isn't in chunk " + this.c + ", " + this.d)) + // .printStackTrace(); + } else { + this.b.remove(entityplayer); + if (this.b.size() == 0) { + long i = (long) this.c + 2147483647L | (long) this.d + 2147483647L << 32; + PlayerManager.b(this.a).b(i); + if (this.g > 0) { + PlayerManager.c(this.a).remove(this); + } + + ((WorldServer) entityplayer.world).u.c(this.c, this.d); + } + + entityplayer.f.remove(this.e); + if (entityplayer.g.contains(this.e)) { + entityplayer.a.b(new Packet50PreChunk(this.c, this.d, false)); + } + } + + } + + public void a(int i, int j, int k) { + if (this.g == 0) { + PlayerManager.c(this.a).add(this); + this.h = this.i = i; + this.j = this.k = j; + this.l = this.m = k; + } + + if (this.h > i) { + this.h = i; + } + + if (this.i < i) { + this.i = i; + } + + if (this.j > j) { + this.j = j; + } + + if (this.k < j) { + this.k = j; + } + + if (this.l > k) { + this.l = k; + } + + if (this.m < k) { + this.m = k; + } + + if (this.g < 10) { + short short1 = (short) (i << 12 | k << 8 | j); + + for (int l = 0; l < this.g; ++l) { + if (this.f[l] == short1) { + return; + } + } + + this.f[this.g++] = short1; + } + + } + + public void a(Packet packet) { + for (int i = 0; i < this.b.size(); ++i) { + EntityPlayer entityplayer = (EntityPlayer) this.b.get(i); + if (entityplayer.g.contains(this.e)) { + entityplayer.a.b(packet); + } + } + + } + + public void a() { + if (this.g != 0) { + int i; + int j; + int k; + if (this.g == 1) { + i = this.c * 16 + this.h; + j = this.j; + k = this.d * 16 + this.l; + this.a((Packet) (new Packet53BlockChange(i, j, k, this.a.world))); + if (Block.p[this.a.world.getTypeId(i, j, k)]) { + this.a(this.a.world.getTileEntity(i, j, k)); + } + } else { + int l; + if (this.g == 10) { + this.j = this.j / 2 * 2; + this.k = (this.k / 2 + 1) * 2; + i = this.h + this.c * 16; + j = this.j; + k = this.l + this.d * 16; + l = this.i - this.h + 1; + int i1 = this.k - this.j + 2; + int j1 = this.m - this.l + 1; + this.a((Packet) (new Packet51MapChunk(i, j, k, l, i1, j1, this.a.world))); + List list = this.a.world.d(i, j, k, i + l, j + i1, k + j1); + + for (int k1 = 0; k1 < list.size(); ++k1) { + this.a((TileEntity) list.get(k1)); + } + } else { + this.a((Packet) (new Packet52MultiBlockChange(this.c, this.d, this.f, this.g, this.a.world))); + + for (i = 0; i < this.g; ++i) { + j = this.c * 16 + (this.g >> 12 & 15); + k = this.g & 255; + l = this.d * 16 + (this.g >> 8 & 15); + if (Block.p[this.a.world.getTypeId(j, k, l)]) { + System.out.println("Sending!"); + this.a(this.a.world.getTileEntity(j, k, l)); + } + } + } + } + + this.g = 0; + } + + } + + private void a(TileEntity tileentity) { + if (tileentity != null) { + Packet packet = tileentity.e(); + if (packet != null) { + this.a(packet); + } + } + + } +} \ No newline at end of file diff --git a/bukkit/src/main/java/net/minecraft/server/PlayerList.java b/bukkit/src/main/java/net/minecraft/server/PlayerList.java new file mode 100644 index 0000000..15dfca3 --- /dev/null +++ b/bukkit/src/main/java/net/minecraft/server/PlayerList.java @@ -0,0 +1,128 @@ +package net.minecraft.server; + +public class PlayerList { + private transient PlayerListEntry[] a = new PlayerListEntry[16]; + private transient int b; + private int c = 12; + private final float d = 0.75F; + private transient volatile int e; + + private static int e(long var0) { + return a((int) (var0 ^ var0 >>> 32)); + } + + private static int a(int var0) { + var0 ^= var0 >>> 20 ^ var0 >>> 12; + return var0 ^ var0 >>> 7 ^ var0 >>> 4; + } + + private static int a(int var0, int var1) { + return var0 & var1 - 1; + } + + public Object a(long var1) { + int var3 = e(var1); + + for (PlayerListEntry var4 = this.a[a(var3, this.a.length)]; var4 != null; var4 = var4.c) { + if (var4.a == var1) { + return var4.b; + } + } + + return null; + } + + public void a(long var1, Object var3) { + int var4 = e(var1); + int var5 = a(var4, this.a.length); + + for (PlayerListEntry var6 = this.a[var5]; var6 != null; var6 = var6.c) { + if (var6.a == var1) { + var6.b = var3; + } + } + + ++this.e; + this.a(var4, var1, var3, var5); + } + + private void b(int var1) { + PlayerListEntry[] var2 = this.a; + int var3 = var2.length; + if (var3 == 1073741824) { + this.c = Integer.MAX_VALUE; + } else { + PlayerListEntry[] var4 = new PlayerListEntry[var1]; + this.a(var4); + this.a = var4; + this.c = (int) ((float) var1 * this.d); + } + } + + private void a(PlayerListEntry[] var1) { + PlayerListEntry[] var2 = this.a; + int var3 = var1.length; + + for (int var4 = 0; var4 < var2.length; ++var4) { + PlayerListEntry var5 = var2[var4]; + if (var5 != null) { + var2[var4] = null; + + PlayerListEntry var6; + do { + var6 = var5.c; + int var7 = a(var5.d, var3); + var5.c = var1[var7]; + var1[var7] = var5; + var5 = var6; + } while (var6 != null); + } + } + + } + + public Object b(long var1) { + PlayerListEntry var3 = this.c(var1); + return var3 == null ? null : var3.b; + } + + final PlayerListEntry c(long var1) { + int var3 = e(var1); + int var4 = a(var3, this.a.length); + PlayerListEntry var5 = this.a[var4]; + + PlayerListEntry var6; + PlayerListEntry var7; + for (var6 = var5; var6 != null; var6 = var7) { + var7 = var6.c; + if (var6.a == var1) { + ++this.e; + --this.b; + if (var5 == var6) { + this.a[var4] = var7; + } else { + var5.c = var7; + } + + return var6; + } + + var5 = var6; + } + + return var6; + } + + private void a(int var1, long var2, Object var4, int var5) { + PlayerListEntry var6 = this.a[var5]; + this.a[var5] = new PlayerListEntry(var1, var2, var4, var6); + if (this.b++ >= this.c) { + this.b(2 * this.a.length); + } + + } + + public PlayerListEntry[] getMap() { + return a; + } +} \ No newline at end of file diff --git a/bukkit/src/main/java/net/minecraft/server/PlayerManager.java b/bukkit/src/main/java/net/minecraft/server/PlayerManager.java new file mode 100644 index 0000000..703b542 --- /dev/null +++ b/bukkit/src/main/java/net/minecraft/server/PlayerManager.java @@ -0,0 +1,181 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.List; + +public class PlayerManager { + private List a = new ArrayList(); + private PlayerList b = new PlayerList(); + private List c = new ArrayList(); + private MinecraftServer d; + private final int[][] e = new int[][]{{1, 0}, {0, 1}, {-1, 0}, {0, -1}}; + public WorldServer world; + + public PlayerManager(MinecraftServer minecraftserver, WorldServer world) { + this.d = minecraftserver; + this.world = world; + } + + public void a() { + for (int i = 0; i < this.c.size(); ++i) { + ((PlayerInstance) this.c.get(i)).a(); + } + + this.c.clear(); + } + + private PlayerInstance a(int i, int j, boolean flag) { + long k = (long) i + 2147483647L | (long) j + 2147483647L << 32; + PlayerInstance playerinstance = (PlayerInstance) this.b.a(k); + if (playerinstance == null && flag) { + playerinstance = new PlayerInstance(this, i, j); + this.b.a(k, playerinstance); + } + + return playerinstance; + } + + public void a(int i, int j, int k) { + int l = i >> 4; + int i1 = k >> 4; + PlayerInstance playerinstance = this.a(l, i1, false); + if (playerinstance != null) { + playerinstance.a(i & 15, j, k & 15); + } + + } + + public void a(EntityPlayer entityplayer) { + int i = (int) entityplayer.locX >> 4; + int j = (int) entityplayer.locZ >> 4; + entityplayer.d = entityplayer.locX; + entityplayer.e = entityplayer.locZ; + int k = 0; + byte b0 = 10; + int l = 0; + int i1 = 0; + this.a(i, j, true).a(entityplayer); + + int j1; + for (j1 = 1; j1 <= b0 * 2; ++j1) { + for (int k1 = 0; k1 < 2; ++k1) { + int[] aint = this.e[k++ % 4]; + + for (int l1 = 0; l1 < j1; ++l1) { + l += aint[0]; + i1 += aint[1]; + this.a(i + l, j + i1, true).a(entityplayer); + } + } + } + + k %= 4; + + for (j1 = 0; j1 < b0 * 2; ++j1) { + l += this.e[k][0]; + i1 += this.e[k][1]; + this.a(i + l, j + i1, true).a(entityplayer); + } + + this.a.add(entityplayer); + } + + public void b(EntityPlayer entityplayer) { + /* + int i = (int) entityplayer.d >> 4; + int j = (int) entityplayer.e >> 4; + + for (int k = i - 10; k <= i + 10; ++k) { + for (int l = j - 10; l <= j + 10; ++l) { + PlayerInstance playerinstance = this.a(k, l, false); + if (playerinstance != null) { + playerinstance.b(entityplayer); + } + } + } + */ + + // rewritten to remove player from all loaded chunks + + PlayerListEntry[] et = this.b.getMap(); + + for(int i = 0; i < et.length; ++i) { + for(PlayerListEntry etr = et[i]; etr != null; etr = etr.c) { + if(etr.b != null) { + ((PlayerInstance)etr.b).b(entityplayer); + } + } + } + + this.a.remove(entityplayer); + } + + private boolean a(int i, int j, int k, int l) { + int i1 = i - k; + int j1 = j - l; + return i1 >= -10 && i1 <= 10 ? j1 >= -10 && j1 <= 10 : false; + } + + public void c(EntityPlayer entityplayer) { + int i = (int) entityplayer.locX >> 4; + int j = (int) entityplayer.locZ >> 4; + double d0 = entityplayer.d - entityplayer.locX; + double d1 = entityplayer.e - entityplayer.locZ; + double d2 = d0 * d0 + d1 * d1; + if (d2 >= 64.0D) { + int k = (int) entityplayer.d >> 4; + int l = (int) entityplayer.e >> 4; + int i1 = i - k; + int j1 = j - l; + if (!this.a(i, j, k, l)) { + this.a(i, j, true).a(entityplayer); + } + + if (!this.a(i - i1, j - j1, i, j)) { + PlayerInstance playerinstance = this.a(i - i1, j - j1, false); + if (playerinstance != null) { + playerinstance.b(entityplayer); + } + } + + if (i1 != 0 || j1 != 0) { + for (int k1 = i - 10; k1 <= i + 10; ++k1) { + for (int l1 = j - 10; l1 <= j + 10; ++l1) { + if (k1 != i || l1 != j) { + if (!this.a(k1, l1, k, l)) { + this.a(k1, l1, true).a(entityplayer); + } + + if (!this.a(k1 - i1, l1 - j1, i, j)) { + PlayerInstance playerinstance = this.a(k1 - i1, l1 - j1, false); + if (playerinstance != null) { + playerinstance.b(entityplayer); + } + } + } + } + } + + entityplayer.d = entityplayer.locX; + entityplayer.e = entityplayer.locZ; + } + } + + } + + public int b() { + return 144; + } + + static MinecraftServer a(PlayerManager playermanager) { + return playermanager.d; + } + + static PlayerList b(PlayerManager playermanager) { + return playermanager.b; + } + + static List c(PlayerManager playermanager) { + return playermanager.c; + } +} \ No newline at end of file diff --git a/bukkit/src/main/resources/default_eagler_config.yml b/bukkit/src/main/resources/default_eagler_config.yml new file mode 100644 index 0000000..e7276b6 --- /dev/null +++ b/bukkit/src/main/resources/default_eagler_config.yml @@ -0,0 +1,11 @@ +enable_password_logins: true +only_allow_registered_users_to_login: true +allow_passwords_without_expiration: true +allow_self_registration: false +allow_self_registration_without_expiration: false +allow_self_change_password: true +allow_self_renew_password: true +allow_self_change_password_expiration: false +allow_self_delete_password: false +default_password_expire_time_seconds: 604800 +maximum_password_expire_time_seconds: 1814400 \ No newline at end of file diff --git a/javascript/Singleplayer_Offline_Download.html b/javascript/Singleplayer_Offline_Download.html deleted file mode 100644 index b23c4d4..0000000 --- a/javascript/Singleplayer_Offline_Download.html +++ /dev/null @@ -1,10189 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -eaglercraft singleplayer test local - - - - - - - - - - - - - - - - - - - -
-

THIS IS AN EXPERIMENTAL SINGLEPLAYER BUILD OF EAGLERCRAFT!

-

EXPECT BUGS!

-

this build is from 5/27/2022

-

(Game will launch in 5)

-
- - - \ No newline at end of file diff --git a/lwjgl-rundir/resources/gui/gui.png b/lwjgl-rundir/resources/gui/gui.png index 81af329e601467a046f59bdff22f450d23b7c7c4..d994eaab2249c324ca3d3603079a88025c76d20d 100644 GIT binary patch literal 16479 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 B+FSqt literal 0 HcmV?d00001 diff --git a/lwjgl-rundir/resources/skins/01.default_steve.png b/lwjgl-rundir/resources/skins/01.default_steve.png new file mode 100644 index 0000000000000000000000000000000000000000..d02b7180ed7567a3ff8313132874483b838aa335 GIT binary patch literal 1832 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 literal 0 HcmV?d00001 diff --git a/lwjgl-rundir/resources/skins/05.cyclist_steve.png b/lwjgl-rundir/resources/skins/05.cyclist_steve.png new file mode 100644 index 0000000000000000000000000000000000000000..7da2003960cf40673a4fd3b02feb01afebc78ddf GIT binary patch literal 1834 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 R1Oos7002ovPDHLkV1jk2ih%$C literal 0 HcmV?d00001 diff --git a/lwjgl-rundir/resources/skins/07.prisoner_steve.png b/lwjgl-rundir/resources/skins/07.prisoner_steve.png new file mode 100644 index 0000000000000000000000000000000000000000..4cc80ac17af5964cf4c5e0406a5008408811bbce GIT binary patch literal 4251 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 xfup#s=^TJpo}rLwrB|sV01rHVxF;37Ht3k literal 0 HcmV?d00001 diff --git a/lwjgl-rundir/resources/skins/08.scottish_steve.png b/lwjgl-rundir/resources/skins/08.scottish_steve.png new file mode 100644 index 0000000000000000000000000000000000000000..25dcfec4a20c5fa65894c1fa9b3cc5d3ea764897 GIT binary patch literal 4647 zcmV+?64>pDP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000M4NkluGtvY4KO8mE!V@@ z`MzUKHJMaD!#x&`b^SX)Urh7?d;w9Q=9*C8{TnCHSMC72B}aq_K}#vT6~>}Cz!wTQ zKp|Y5;q&MHw+;scngZ_4^1w4B?*A1N-d1julX4-6aXF2oXI6Lf!$H`@D%WW6Q_Tc6^p z_6e8k12P#9n+j0f1wE%C+nfaw~fNVgn1knP0L{+M6sD{@82P7$N)<8Po`+xw)1jB)M-t%4JP@~%P(Dj&F zeTLpjH+8p8-<@9~_}h0dH1K2P2umnifvL(Rh{4k4`Uuld;H^mI-?fPPylpTx!Lw#T z1kgLMlm4McC`!ngCL2EW1)e?8=34bM_G zZ-hP~B|$w<5prxO4Q!`m7iq*kWi!vRN)N&;^5@Z)0C@k<9h{#$!}x_NmMO^V7Bg;* znZNI)=_;U!0}>KksE_akeD3LMOoEnBscK5_-t`&Q+`ODPXfj=|Q)@<8-8RAbab7>? zk=NDUcld>)RF+$47*wksu@li z+*NNF_GI?KT%3f1OrS{A3rPH_L0 z2l>^rzu=XTCsObqdte)<$4;h=68E15aVJy7_jyN`M z(cnPd23%IQaX|et^5|@~p@M58}{I{`0*Y&gnT2pXzbadVW<2X+B z<2Y`)9;MNhGDg?MB|5tpO%v%oZ+I8*Zq*srET(OTakq)RUNEiYn93J9*{I=_JInR6 z{yZQn*YqkgkbgBbqo#; zro7M|4gJwc;_ab-?Lx+*-@@!6sprx z>Z9kZPP=^go(D4#5JIH&8^=**%aSrCilPi4+sV(FUD${ z92%l>WR%r=f1TQzn3&-EyEdzBbw&TIzQMh>t_3bN>-Mhk&Vt^_fD++t!cIJ|_W}08i$$ d$(K6*Hvp{wuWWVX$K(J2002ovPDHLkV1i@Y+qwV% literal 0 HcmV?d00001 diff --git a/lwjgl-rundir/resources/skins/09.dev_steve.png b/lwjgl-rundir/resources/skins/09.dev_steve.png new file mode 100644 index 0000000000000000000000000000000000000000..8daf1b3aa4e70ab24fe28be6037b8711e5f4a5f7 GIT binary patch literal 1630 zcmV-k2BG8jWvzqOallP49otuz;Jm)eRV9AR;gpmou$zQ3GJ`__NY+ z5H5{R#m4XJz6Xn;@m7S7_OalqVMd+)MF)VvTN_&}ZB8N>oWbA>cT*E{5k7jabUZlp zE=$-v-DLpO4+C@TUto=GexxI&DmXY=a=sJ`&6 zwAdcBSWJK#Q}%;hJAAZ+KV6s00D!kXJyL&BLU^KN0<}P*07VY0C|!8Nv|zbe2-|_M z9awG_hG~Vs)R+KOF(9fH9u4~EB@9MEn3fv*ckjgh-8+3sn-+Sv4r1@2oG+l~Veg?F zdbbY3Ff9NG=TA!=6Mj)R!A%dh6Jsp2fKplG;OjduQz*i5GcZikN6HZR@rM%tfX&Zr zgjT*8C@I6auYG(3w%`%%6TC9PFaVaYF#9y^|n4izTyZ!lape>v6 z8DLoe5srR+0{z=I0zmbiF(}Ui08HO3hHKv$`UXS=!HNqABL z0QMdI7CcC%v?dJpKL-GK^o`Hw7l35P{ac3OdBJZ7LQseZ7cN|Ac;F2iHqhXcJ*i{+ zPL5(^WTfGNu3vAQ7xeY@h3C^+Z+MKpNR0T6}4FBq>KVPmuvr%SgPfRsONT1$5@%W+*7g+ig`Y;MMBNm=ZH z7rrCLspxxWwK%)iodecPQi(PxaI zf9Nz!OTe@Q`iD+8WD(~)^@>uRBIc-+s-J<+kp-ej!)tm&+qS_u2V)Gc96Rgh6m4xF zB518^4AM0B;HionLJlpw~rvgwhjc9zYXq<@PI5t>h%+6+gK{GBgISKXmKcGNSEHF3I6fjNEBm*inYsBfP zl`DgrG63BYwW2_r34oYwe!ZlMl!9Rxsjn=eekHntl;x&C5Jdh4OmiPXifly`1PUE! zn}d`Bp#;W74LfSm;?dFY{@0$KmVvA|#EP?2rBdCO2})@Q;eVBqa(0I)7~>$)AkuN+ zD}Sz44GSvNXDK=%@(Fo5Auz9_<;o|t=%=)3=!^Or(%L^u#5qPCctx2?X&44?DuEMA zFCFp|l%9b=I0~3RAkr|nPooa`69Aw$(;HTP0B}5I(+}6IsZ%8ArsyAiR!YGXW=MDC z$#8sfd;&KLB`k4m+$fZg&DfZ@HieZdmWF1~bQI_wJq!R?6FIz;62r2w7wufVE1r|D z$b*}9&AiN?2U6F}bajGp0|0|_@v~5CGphzbT7j{@ptS<$hCdcyGkIkKOS(ERRV?Da z$s%%D2UjNxSl-=*f3IA_@?{-0jx`m4WIT-~8ucjad0uFbhHzrlHP7V0jauhZeDF;; z!yln@^{&_&vR&}td1loBoQsDsvuXgMtsS~N165XmGcAKL77FFI=b!@0C;dTtdln^6 cHsxdf2O>eHz9@%34*&oF07*qoM6N<$f>x2+E&u=k literal 0 HcmV?d00001 diff --git a/lwjgl-rundir/resources/skins/10.herobrine.png b/lwjgl-rundir/resources/skins/10.herobrine.png new file mode 100644 index 0000000000000000000000000000000000000000..a449fc1671525bca3c3e6d97ba5227c1a85cb168 GIT binary patch literal 1605 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 literal 0 HcmV?d00001 diff --git a/lwjgl-rundir/resources/skins/12.trump.png b/lwjgl-rundir/resources/skins/12.trump.png new file mode 100644 index 0000000000000000000000000000000000000000..0e48b12de8180573b22e08b79a79d7bcd45b0449 GIT binary patch literal 1522 zcmVJx9$=~z?%g5f9-NMVB*hX$hfoexfy%hq@R$KG9y ziORA$nnp=%XsMa{Vj=F-#V^# z_XE({c6!lJ&t2u3pu*=T7`~uW97v7>hKmF!;#uMQ*Qlx*AEALrO3W~uvm?UzwGBI2 z+Wm3|LeEMyK`>RJe^6^E=+~ufcUS2zXg8G+Q$sdgAL9imfl-a&Jrs+|hvupVMkQM7| z*Bx|BX#~Sj9`jc4_g_<7`|&Ic|Dk_0GH{cv>+4uCc7vSh5w<+t$Y9_y@jnOW%ig|z z`hWd})n#6C9(|b3jxOdV&Kyg`& z;Yif7d6LKb4z+XaKr7Ghd6#1cTG@A~T`#EiZ1E@jb=9`~{Orr`BmkOEd|)ee2CmFE zWd6EpJ-y3mK5(Un1sgRsZA(my#{rla*SH@14NcQhY6q(yuUb=|u<8z7Zk6eH%y6yK zt#Uhf-g5tv+MS8cb4LKsG>z+@9!$B6-6tBwD8~*>6h%Q%6x*~~ z%I>iDr;iq#rN~oAk*81x$mLcSw--obzyl{{?zkx3;N37_DW&SER~B4|%z(kGryOQ3 zX4ngOYIi2mCufoy%ouFsl<4Jjt6ci(y%c@JGcVwBt8`sD$?1~$s;_163^f~flg+{G zO2_wKuxZC(0DefGWkd7p*o^_XvQffdXPOVC&&zs6QS=*86a|-C#pPDfG>u~N7-9lV z(~$jA03pwbu^C8a`b|3y>pB61cz3x~MsIcAy9;D1F1L!;>tzjj6qAS7>&4|(`R2p7 zEsYc7^dOc=pJ~sG447F0Y8uJ*w~k~y@N_(8c-vNbBi2mEW42r_*DN1r0%ay1!#^WJ-bY$+{nTJHR;Qk6_^qnowT zM~0>rKMUN8h!kp+hn;Q}uV)r0(C4o8kLcs6cx*wh{pE|5$`V+(1CPgLt^Ms z6^XT*SC$U#1Y4yJKqo{cs6`tQGg!t+XlzwA1Dv_LI8K}h%&9*S9=stc4*!pnBttAO~a^q@4eLV#963|OP*I9q7CORpc zZ%YCqp}#w2*%I9LNijiHmVLiC9*_A>V46i7<^R&moY2#NZcl(TGuKexsZD^sy3$bJ zsTIMJS;Ux}W%|A=%ZS$DcZ-eJDxo6?*xSuxOwPh_9OgGBXR)`N?^y8s`#loU-fd72 zj`vd6uJ@n$U7#whvMe*uQWOP2xR=YMeW5ve(|`AvpP%=g|L*lG&hLs102mC5SbDJl z(<~w$kHIvHNXc2DEfff1WlQiqF&@Q8@O=ABQeZGFGOHhNVpnPl0^Yp;REr)h3Az<4 z@A8>&Ue|R7LFqc5lD#83>&#j+Fi+7i_HTkWGrm_~<~${YX9$^qdQi9=Ok!&!3@e3GTC=zab5%#c`qm zv_>S8N%m5-vCaflt4LCBYb1W2_fPLV^sO)O^$6ZyuioH^0993?s_OqBeKdj&o`yQV zn#4zsq1(q*N$U3#Hzt$+5W@RDY*46F?tzWm)zd zw6?ZZ^JtvQq@fv>%k{G5|H6@k=#KZIgIBUTh5$uTyw3iCtQM5LlEe3H>lI8$;fBPk z>u!StvP79_X!FYMl`ze)F(HK+itw6pY!x9t%MVXN7jzs*5_Db1^z1a_|MGSP5$>?_ zbLav9;6Rel|MerKAx=$!?b-x*2_(^b07*qoM6N<$f<-I4`~Uy| literal 0 HcmV?d00001 diff --git a/lwjgl-rundir/resources/skins/14.creeper.png b/lwjgl-rundir/resources/skins/14.creeper.png new file mode 100644 index 0000000000000000000000000000000000000000..9bdeb9e76d340a6f0ad53d67437933296b946234 GIT binary patch literal 3145 zcmV-P47T%$P)7C|w-|g&Y{y3Kl zj7!5*Qt12NJ@-A|@_f(pyzh6f#sqO~$K}zYwww60n8!&YORZPS54Swaw{BlW$KZOUf5XBgVjL1mWYJmzQe$bsspMcgiV=4hKa3dA zdkNft;*cFnpf-m$6!1XPYOK&=?iW7G^Q9-TM}m(%0Aiwq;n5THogCmB3%|}CQ|>0E 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 literal 0 HcmV?d00001 diff --git a/lwjgl-rundir/resources/skins/15.zombie.png b/lwjgl-rundir/resources/skins/15.zombie.png new file mode 100644 index 0000000000000000000000000000000000000000..9c57fb05833c1ed907faa72c00d07f098bc78712 GIT binary patch literal 2472 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 Y7d#n9TL5ki;Q#;t07*qoM6N<$f@vevzyJUM literal 0 HcmV?d00001 diff --git a/lwjgl-rundir/resources/skins/18.mooshroom.png b/lwjgl-rundir/resources/skins/18.mooshroom.png new file mode 100644 index 0000000000000000000000000000000000000000..2e80e39f2247f0cc09782578bd54dcb05c58b692 GIT binary patch literal 1679 zcmV;A25|X_P)4R^nq2mTcZ_@4u+cK_C`TVy5@ZbeYA zSD$+5*pex*B5hWemVEaN!>CJ$Tfun%@#D^@tj^aP{RjDA?l*TgTZP7owCTKZ8Gyyb zMF2V{r(BsE{RcU8=#Z~JP+Xv@h(giBqsQELj^r${X;L6BCBs)Plg(!7`{t~#1hxtd zUDwfdop?M>=j0S!S_=U0pFKl1o2BR8J<^GBUutYQ4*2Q#38oJpCKL*hmy)AamZMe{ zfKsVMsZ`?m@JD?1G{;dZOI}Jsp%6bDIl^DxeP1_?a>tH0)mOZ>04O;Baq4tUJztSF z*=&}~iD6$~pk#jk;uHY8r>1%K`RKX|RS-5c2W+I4uh;wp-LKyH&ABO^R~_z=JiL=fz~_q-*r`WiH2ycP}!n*Upc#%gjJ(=<7M z{yYZ`8~|W`exCdH@7K-QWHKoMEXyL6TA+xeGj)>^k`RzPdj@9bh{a-<@4bs*7_6?Y zQYaJ%fAuw=G~k3+wXdm8@UdN{Yit}NIlbYCT9(Dzrb+adYuE^urHv*8I)WF2{guEB z4qyRqgu?(73I&R@vqYQJ@hbK;3_z>U$jfpCo}**X7T(YcJ13`DcG5t1(jeE{i`Bay z!!T&OdKF!0M50m3vvVx?zUdc+9OpwoUfM*2 zAbj{kC={?Pi)d;AfK^8(np&Vm2v(gG*~r<%%h*@L0qV@pTk=#r2mvpgT~x}tv=-Nh zyp)85Aa?OW1=_I~OCO)~$yZmFBCum<$d!L`{W=jLNGHZ?hAlZd=CdnEzqg7hxs9Aj z>Z(L3BMie}``9>->@rbDS%tanz&c)(lKja}5rh2{!{Hi-gQi3M#w^>Ata>{}j4S)jbkg0~ zSS&_5F;3gX3qE!mxjz@y&% zEcf=(IXUHnvY1J_X9|kl>*=p&f1`0*3xb%J#npd{i;HAV47>j+HdaKnWIdrfwLnA& zwgEZ2%yv)x-sQkDA_U#31@cnTomyb`)N~Eh6?!VWUtfPC`an*cqSzR&=EfQV$~svm`TzOq|<4)O{%BAk^K!x z@Y@}28!jE*@$<{)%S41=Syp;^;gmHTC#u)7lQv3Tl|fbd49{~?N>0DTu;aClr@6Ya Z{{Sjy`KSw6Z{7d^002ovPDHLkV1g|CDQN%z literal 0 HcmV?d00001 diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/anvil/SaveFormatOld.java b/src/lwjgl/java/net/lax1dude/eaglercraft/anvil/SaveFormatOld.java index c081532..0c682ad 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/anvil/SaveFormatOld.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/anvil/SaveFormatOld.java @@ -29,7 +29,7 @@ public class SaveFormatOld implements ISaveFormat { return "Old Format"; } - public List getWorldList() { + public List getWorldList(IProgressUpdate progress) { ArrayList arraylist = new ArrayList(); for (int i = 0; i < 5; i++) { String s = (new StringBuilder()).append("World").append(i + 1).toString(); diff --git a/src/main/java/net/lax1dude/eaglercraft/ConfigConstants.java b/src/main/java/net/lax1dude/eaglercraft/ConfigConstants.java index de107f7..868a69a 100644 --- a/src/main/java/net/lax1dude/eaglercraft/ConfigConstants.java +++ b/src/main/java/net/lax1dude/eaglercraft/ConfigConstants.java @@ -4,7 +4,7 @@ public class ConfigConstants { public static boolean profanity = false; - public static final String version = "22w21a-SNAPSHOT"; + public static final String version = "22w22a"; public static final String mainMenuString = "eaglercraft beta-" + version; public static final String forkMe = "https://github.com/LAX1DUDE/eaglercraft"; diff --git a/src/main/java/net/lax1dude/eaglercraft/EaglerProfile.java b/src/main/java/net/lax1dude/eaglercraft/EaglerProfile.java new file mode 100644 index 0000000..c09213a --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/EaglerProfile.java @@ -0,0 +1,389 @@ +package net.lax1dude.eaglercraft; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import net.minecraft.client.Minecraft; +import net.minecraft.src.NBTTagCompound; +import net.minecraft.src.RenderEngine; + +public class EaglerProfile { + + public static class EaglerProfileSkin { + public String name; + public byte[] data; + public boolean slim; + public int glTex; + public EaglerProfileSkin(String name, byte[] data, boolean slim, int glTex) { + this.name = name; + this.data = data; + this.slim = slim; + this.glTex = glTex; + } + } + + public static String username; + public static int presetSkinId; + public static int customSkinId; + + public static String myChannel; + + public static final int SKIN_DATA_SIZE = 64*32*4; + public static ArrayList skins = new ArrayList(); + + public static final EaglercraftRandom rand; + + public static byte[] getSelfSkinPacket() { + if(presetSkinId == -1) { + byte[] d = skins.get(customSkinId).data; + byte[] d2 = new byte[1 + d.length]; + d2[0] = (byte) 1; + System.arraycopy(d, 0, d2, 1, d.length); + return d2; + }else { + return new byte[] { (byte)0, (byte)presetSkinId }; + } + } + + public static String[] concatArrays(String[] a, String[] b) { + String[] r = new String[a.length + b.length]; + System.arraycopy(a, 0, r, 0, a.length); + System.arraycopy(b, 0, r, a.length, b.length); + return r; + } + + public static int addSkin(String name, byte[] data, boolean slim) { + int i = -1; + for(int j = 0, l = skins.size(); j < l; ++j) { + if(skins.get(j).name.equalsIgnoreCase(name)) { + i = j; + break; + } + } + + if(data.length != SKIN_DATA_SIZE) { + return -1; + } + + int im = Minecraft.getMinecraft().renderEngine.allocateAndSetupTexture(data, 64, 32); + if(i == -1) { + i = skins.size(); + skins.add(new EaglerProfileSkin(name, data, slim, im)); + }else { + skins.get(i).glTex = im; + skins.get(i).data = data; + skins.get(i).slim = slim; + } + return i; + + } + + private static class CachedSkin { + + protected final String username; + protected UserSkin skin; + protected long age; + + protected CachedSkin(String username, UserSkin skin) { + this.username = username; + this.skin = skin; + this.age = System.currentTimeMillis(); + } + + } + + public static enum EnumSkinType { + PRESET, CUSTOM_LEGACY + } + + public static interface UserSkin { + + abstract EnumSkinType getSkinType(); + abstract int getSkin(); + abstract int getTexture(); + abstract void free(); + + } + + private static class UserPresetSkin implements UserSkin { + + protected final int skinType; + + protected UserPresetSkin(int skin) { + this.skinType = skin; + } + + @Override + public EnumSkinType getSkinType() { + return EnumSkinType.PRESET; + } + + @Override + public int getSkin() { + return skinType; + } + + @Override + public int getTexture() { + return (skinType >= defaultOptionsTextures.length || skinType < 0) ? -1 : defaultOptionsTextures[skinType].getTexturePointer(); + } + + @Override + public void free() { + } + + } + + private static class UserCustomSkin implements UserSkin { + + protected final byte[] data; + protected int glTexture; + + protected UserCustomSkin(byte[] data) { + this.data = data; + this.glTexture = -1; + } + + @Override + public EnumSkinType getSkinType() { + return EnumSkinType.CUSTOM_LEGACY; + } + + @Override + public int getSkin() { + return -1; + } + + @Override + public int getTexture() { + RenderEngine r = Minecraft.getMinecraft().renderEngine; + if(glTexture == -1) { + glTexture = r.allocateAndSetupTexture(data, 64, 32); + } + return glTexture; + } + + @Override + public void free() { + RenderEngine r = Minecraft.getMinecraft().renderEngine; + r.deleteTexture(glTexture); + glTexture = -1; + } + + } + + private static class WaitingSkin { + + protected final int cookie; + protected final String username; + protected final long requestStartTime; + + protected WaitingSkin(int cookie, String username) { + this.cookie = cookie; + this.username = username; + this.requestStartTime = System.currentTimeMillis(); + } + + } + + private static final Map multiplayerWaitingSkinCache = new HashMap(); + private static final Map multiplayerSkinCache = new HashMap(); + private static final long maxSkinAge = 1000l * 60l * 5l; + + private static final UserSkin defaultSkin = new UserPresetSkin(0); + + private static int skinRequestId = 0; + + public static int beginSkinRequest(String un) { + int ret = skinRequestId++; + if(skinRequestId >= 65536) { + skinRequestId = 0; + } + multiplayerWaitingSkinCache.put(ret, new WaitingSkin(ret, un)); + return ret; + } + + public static boolean skinRequestPending(String un) { + return multiplayerWaitingSkinCache.containsKey(un); + } + + public static UserSkin getUserSkin(String un) { + CachedSkin cs = multiplayerSkinCache.get(un); + if(cs == null) { + return null; + }else { + cs.age = System.currentTimeMillis(); + return cs.skin; + } + } + + public static void processSkinResponse(byte[] dat) { + if(dat.length >= 3) { + int cookie = (((int)dat[0] & 0xFF) << 8) | ((int)dat[1] & 0xFF); + WaitingSkin st = multiplayerWaitingSkinCache.remove(cookie); + if(st != null) { + int t = (int)dat[2] & 0xFF; + if(t == 0) { + if(dat.length == 4) { + multiplayerSkinCache.put(st.username, new CachedSkin(st.username, new UserPresetSkin((int)dat[3] & 0xFF))); + }else { + System.out.println("Recieved a PRESET skin of the wrong size (" + (dat.length - 3) + ") for player " + st + "."); + } + }else if(t == 1) { + if(dat.length == 3 + SKIN_DATA_SIZE) { + byte[] datt = new byte[SKIN_DATA_SIZE]; + System.arraycopy(dat, 3, datt, 0, SKIN_DATA_SIZE); + multiplayerSkinCache.put(st.username, new CachedSkin(st.username, new UserCustomSkin(datt))); + }else { + System.out.println("Recieved a CUSTOM_LEGACY skin of the wrong size (" + (dat.length - 3) + ") for player " + st + "."); + } + }else { + System.out.println("Unsupported skin type '" + t + "' was recieved from server for player " + st + "."); + } + } + } + } + + public static void freeSkins() { + long millis = System.currentTimeMillis(); + Iterator skns = multiplayerSkinCache.values().iterator(); + while(skns.hasNext()) { + CachedSkin cs = skns.next(); + if(millis - cs.age > maxSkinAge) { + cs.skin.free(); + skns.remove(); + } + } + Iterator skns2 = multiplayerWaitingSkinCache.values().iterator(); + while(skns2.hasNext()) { + WaitingSkin cs = skns2.next(); + if(millis - cs.requestStartTime > 10000l) { + skns2.remove(); + } + } + } + + public static void freeUserSkin(String un) { + CachedSkin cs = multiplayerSkinCache.remove(un); + if(cs != null) { + cs.skin.free(); + } + } + + public static void freeAllSkins() { + Iterator skns = multiplayerSkinCache.values().iterator(); + while(skns.hasNext()) { + skns.next().skin.free(); + } + multiplayerWaitingSkinCache.clear(); + multiplayerSkinCache.clear(); + } + + static { + String[] usernameDefaultWords = ConfigConstants.profanity ? new String[] { + "Eagler", + "Eagler", + "Bitch", + "Cock", + "Milf", + "Milf", + "Yeer", + "Groon", + "Eag", + "Deevis", + "Chode", + "Deev", + "Deev", + "Fucker", + "Fucking", + "Dumpster", + "Dumpster", + "Cum", + "Chad", + "Egg", + "Fudgler", + "Fudgli", + "Yee", + "Yee", + "Yee", + "Yeet", + "Flumpter", + "Darvy", + "Darver", + "Darver", + "Fuck", + "Fuck", + "Frick", + "Eagler", + "Vigg", + "Vigg", + "Cunt", + "Darvig" + } : new String[] { + "Yeeish", + "Yeeish", + "Yee", + "Yee", + "Yeer", + "Yeeler", + "Eagler", + "Eagl", + "Darver", + "Darvler", + "Vool", + "Vigg", + "Vigg", + "Deev", + "Yigg", + "Yeeg" + }; + + rand = new EaglercraftRandom(); + + do { + username = usernameDefaultWords[rand.nextInt(usernameDefaultWords.length)] + usernameDefaultWords[rand.nextInt(usernameDefaultWords.length)] + (10 + rand.nextInt(90)); + }while(username.length() > 16); + + presetSkinId = rand.nextInt(GuiScreenEditProfile.defaultOptions.length); + myChannel = username + "_" + (100 + rand.nextInt(900)); + customSkinId = -1; + } + + public static void loadFromStorage() { + if(!LocalStorageManager.profileSettingsStorage.hasNoTags()) { + presetSkinId = LocalStorageManager.profileSettingsStorage.getInteger("ps"); + customSkinId = LocalStorageManager.profileSettingsStorage.getInteger("cs"); + username = LocalStorageManager.profileSettingsStorage.getString("name"); + myChannel = username + "_" + (100 + rand.nextInt(900)); + NBTTagCompound n = LocalStorageManager.profileSettingsStorage.getCompoundTag("skins"); + for(Object s : NBTTagCompound.getTagMap(n).keySet()) { + String s2 = (String)s; + addSkin(s2, n.getByteArray(s2), false); + } + } + } + + public static final TextureLocation[] defaultOptionsTextures = new TextureLocation[] { + new TextureLocation("/skins/01.default_steve.png"), + new TextureLocation("/skins/02.tennis_steve.png"), + new TextureLocation("/skins/03.tuxedo_steve.png"), + new TextureLocation("/skins/04.athlete_steve.png"), + new TextureLocation("/skins/05.cyclist_steve.png"), + new TextureLocation("/skins/06.boxer_steve.png"), + new TextureLocation("/skins/07.prisoner_steve.png"), + new TextureLocation("/skins/08.scottish_steve.png"), + new TextureLocation("/skins/09.dev_steve.png"), + new TextureLocation("/skins/10.herobrine.png"), + new TextureLocation("/skins/11.slime.png"), + new TextureLocation("/skins/12.trump.png"), + new TextureLocation("/skins/13.notch.png"), + new TextureLocation("/skins/14.creeper.png"), + new TextureLocation("/skins/15.zombie.png"), + new TextureLocation("/skins/16.pig.png"), + new TextureLocation("/skins/17.squid.png"), + new TextureLocation("/skins/18.mooshroom.png") + }; + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/GuiScreenEditProfile.java b/src/main/java/net/lax1dude/eaglercraft/GuiScreenEditProfile.java new file mode 100644 index 0000000..87bdfee --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/GuiScreenEditProfile.java @@ -0,0 +1,401 @@ +package net.lax1dude.eaglercraft; + +import net.lax1dude.eaglercraft.EaglerProfile.EaglerProfileSkin; +import net.minecraft.client.Minecraft; +import net.minecraft.src.GuiButton; +import net.minecraft.src.GuiScreen; +import net.minecraft.src.GuiTextField; +import net.minecraft.src.ModelBiped; +import net.minecraft.src.NBTTagCompound; +import net.minecraft.src.RenderHelper; +import net.minecraft.src.Session; +import net.minecraft.src.StringTranslate; + +public class GuiScreenEditProfile extends GuiScreen { + + private GuiScreen parent; + private GuiTextField username; + + private boolean dropDownOpen = false; + private String[] dropDownOptions; + private int slotsVisible = 0; + private int selectedSlot = 0; + private int scrollPos = -1; + private int skinsHeight = 0; + private boolean dragging = false; + private int mousex = 0; + private int mousey = 0; + + private static final TextureLocation gui = new TextureLocation("/gui/gui.png"); + + public static final String[] defaultOptions = new String[] { + "Default Steve", + "Tennis Steve", + "Tuxedo Steve", + "Athlete Steve", + "Cyclist Steve", + "Boxer Steve", + "Prisoner Steve", + "Scottish Steve", + "Developer Steve", + "Herobrine", + "Slime", + "Trump", + "Notch", + "Creeper", + "Zombie", + "Pig", + "Squid", + "Mooshroom" + }; + + public static final TextureLocation[] defaultOptionsTextures = new TextureLocation[] { + new TextureLocation("/skins/01.default_steve.png"), + new TextureLocation("/skins/02.tennis_steve.png"), + new TextureLocation("/skins/03.tuxedo_steve.png"), + new TextureLocation("/skins/04.athlete_steve.png"), + new TextureLocation("/skins/05.cyclist_steve.png"), + new TextureLocation("/skins/06.boxer_steve.png"), + new TextureLocation("/skins/07.prisoner_steve.png"), + new TextureLocation("/skins/08.scottish_steve.png"), + new TextureLocation("/skins/09.dev_steve.png"), + new TextureLocation("/skins/10.herobrine.png"), + new TextureLocation("/skins/11.slime.png"), + new TextureLocation("/skins/12.trump.png"), + new TextureLocation("/skins/13.notch.png"), + new TextureLocation("/skins/14.creeper.png"), + new TextureLocation("/skins/15.zombie.png"), + new TextureLocation("/skins/16.pig.png"), + new TextureLocation("/skins/17.squid.png"), + new TextureLocation("/skins/18.mooshroom.png") + }; + + protected String screenTitle = "Edit Profile"; + + public GuiScreenEditProfile(GuiScreen parent) { + this.parent = parent; + reconcatDD(); + } + + private void reconcatDD() { + String[] n = new String[EaglerProfile.skins.size()]; + for(int i = 0; i < n.length; ++i) { + n[i] = EaglerProfile.skins.get(i).name; + } + + this.dropDownOptions = EaglerProfile.concatArrays(n, defaultOptions); + } + + private GuiButton button0, button1, button2, button10, button11, button12; + + public void initGui() { + super.initGui(); + EaglerAdapter.enableRepeatEvents(true); + StringTranslate var1 = StringTranslate.getInstance(); + this.screenTitle = var1.translateKey("profile.title"); + this.username = new GuiTextField(this.fontRenderer, this.width / 2 - 20 + 1, this.height / 6 + 24 + 1, 138, 20, EaglerProfile.username); + this.username.field_22081_b = true; + selectedSlot = EaglerProfile.presetSkinId == -1 ? EaglerProfile.customSkinId : (EaglerProfile.presetSkinId + EaglerProfile.skins.size()); + //this.buttonList.add(new GuiButton(0, this.width / 2 - 100, 140, "eeeee")); + this.controlList.add(button0 = new GuiButton(200, this.width / 2 - 100, this.height / 6 + 168, var1.translateKey("gui.done"))); + this.controlList.add(button1 = new GuiButton(2, this.width / 2 - 21, this.height / 6 + 110, 71, 20, var1.translateKey("profile.addSkin"))); + this.controlList.add(button2 = new GuiButton(3, this.width / 2 - 21 + 71, this.height / 6 + 110, 72, 20, var1.translateKey("profile.clearSkin"))); + //this.buttonList.add(new GuiButton(200, this.width / 2, this.height / 6 + 72, 150, 20, var1.translateKey("gui.done"))); + } + + private static ModelBiped playerModel = null; + + public void drawScreen(int mx, int my, float par3) { + StringTranslate var1 = StringTranslate.getInstance(); + this.drawDefaultBackground(); + this.drawCenteredString(this.fontRenderer, this.screenTitle, this.width / 2, 15, 16777215); + this.drawString(this.fontRenderer, var1.translateKey("profile.screenname"), this.width / 2 - 20, this.height / 6 + 8, 10526880); + this.drawString(this.fontRenderer, var1.translateKey("profile.playerSkin"), this.width / 2 - 20, this.height / 6 + 66, 10526880); + + mousex = mx; + mousey = my; + + int skinX = this.width / 2 - 120; + int skinY = this.height / 6 + 8; + int skinWidth = 80; + int skinHeight = 130; + + drawRect(skinX, skinY, skinX + skinWidth, skinY + skinHeight, -6250336); + drawRect(skinX + 1, skinY + 1, skinX + skinWidth - 1, skinY + skinHeight - 1, 0xff000015); + + this.username.drawTextBox(); + if(dropDownOpen) { + super.drawScreen(0, 0, par3); + }else { + super.drawScreen(mx, my, par3); + } + + skinX = this.width / 2 - 20; + skinY = this.height / 6 + 82; + skinWidth = 140; + skinHeight = 22; + + drawRect(skinX, skinY, skinX + skinWidth, skinY + skinHeight, -6250336); + drawRect(skinX + 1, skinY + 1, skinX + skinWidth - 21, skinY + skinHeight - 1, -16777216); + drawRect(skinX + skinWidth - 20, skinY + 1, skinX + skinWidth - 1, skinY + skinHeight - 1, -16777216); + + EaglerAdapter.glColor4f(1f, 1f, 1f, 1f); + gui.bindTexture(); + drawTexturedModalRect(skinX + skinWidth - 18, skinY + 3, 0, 240, 16, 16); + + this.fontRenderer.drawStringWithShadow(dropDownOptions[selectedSlot], skinX + 5, skinY + 7, 14737632); + + skinX = this.width / 2 - 20; + skinY = this.height / 6 + 103; + skinWidth = 140; + skinHeight = (this.height - skinY - 10); + slotsVisible = (skinHeight / 10); + if(slotsVisible > dropDownOptions.length) slotsVisible = dropDownOptions.length; + skinHeight = slotsVisible * 10 + 7; + skinsHeight = skinHeight; + if(scrollPos == -1) { + scrollPos = selectedSlot - 2; + } + if(scrollPos > (dropDownOptions.length - slotsVisible)) { + scrollPos = (dropDownOptions.length - slotsVisible); + } + if(scrollPos < 0) { + scrollPos = 0; + } + if(dropDownOpen) { + drawRect(skinX, skinY, skinX + skinWidth, skinY + skinHeight, -6250336); + drawRect(skinX + 1, skinY + 1, skinX + skinWidth - 1, skinY + skinHeight - 1, -16777216); + for(int i = 0; i < slotsVisible; i++) { + if(i + scrollPos < dropDownOptions.length) { + if(selectedSlot == i + scrollPos) { + drawRect(skinX + 1, skinY + i*10 + 4, skinX + skinWidth - 1, skinY + i*10 + 14, 0x77ffffff); + }else if(mx >= skinX && mx < (skinX + skinWidth - 10) && my >= (skinY + i*10 + 5) && my < (skinY + i*10 + 15)) { + drawRect(skinX + 1, skinY + i*10 + 4, skinX + skinWidth - 1, skinY + i*10 + 14, 0x55ffffff); + } + this.fontRenderer.drawStringWithShadow(dropDownOptions[i + scrollPos], skinX + 5, skinY + 5 + i*10, 14737632); + } + } + int scrollerSize = skinHeight * slotsVisible / dropDownOptions.length; + int scrollerPos = skinHeight * scrollPos / dropDownOptions.length; + drawRect(skinX + skinWidth - 4, skinY + scrollerPos + 1, skinX + skinWidth - 1, skinY + scrollerPos + scrollerSize, 0xff888888); + } + + int xx = this.width / 2 - 80; + int yy = this.height / 6 + 130; + skinX = this.width / 2 - 120; + skinY = this.height / 6 + 8; + skinWidth = 80; + skinHeight = 130; + + int id = selectedSlot - EaglerProfile.skins.size(); + + if(id < 0) { + Minecraft.getMinecraft().renderEngine.bindTexture(EaglerProfile.skins.get(selectedSlot).glTex); + }else { + defaultOptionsTextures[id].bindTexture(); + } + + EaglerAdapter.glEnable(EaglerAdapter.GL_TEXTURE_2D); + EaglerAdapter.glDisable(EaglerAdapter.GL_BLEND); + EaglerAdapter.glDisable(EaglerAdapter.GL_CULL_FACE); + EaglerAdapter.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + EaglerAdapter.glPushMatrix(); + EaglerAdapter.glTranslatef((float) xx, (float) (yy - 80), 100.0F); + EaglerAdapter.glScalef(50.0f, 50.0f, 50.0f); + EaglerAdapter.glRotatef(180.0f, 1.0f, 0.0f, 0.0f); + RenderHelper.enableStandardItemLighting(); + EaglerAdapter.glScalef(1.0F, -1.0F, 1.0F); + EaglerAdapter.glTranslatef(0.0F, 1.0F, 0.0F); + EaglerAdapter.glRotatef(((yy - my) * -0.06f), 1.0f, 0.0f, 0.0f); + EaglerAdapter.glRotatef(((xx - mx) * 0.06f), 0.0f, 1.0f, 0.0f); + EaglerAdapter.glTranslatef(0.0F, -1.0F, 0.0F); + + if(playerModel == null) { + playerModel = new ModelBiped(0.0f); + playerModel.blockTransparentSkins = true; + } + + playerModel.render(0.0f, 0.0f, (float)(System.currentTimeMillis() % 100000) / 50f, ((xx - mx) * 0.06f), ((yy - my) * -0.1f), 0.0625F); + + EaglerAdapter.glPopMatrix(); + EaglerAdapter.glEnable(EaglerAdapter.GL_BLEND); + EaglerAdapter.glEnable(EaglerAdapter.GL_CULL_FACE); + + } + + public void handleMouseInput() { + super.handleMouseInput(); + if(dropDownOpen) { + int var1 = EaglerAdapter.mouseGetEventDWheel(); + if(var1 < 0) { + scrollPos += 3; + } + if(var1 > 0) { + scrollPos -= 3; + } + if(scrollPos < 0) { + scrollPos = 0; + } + if(scrollPos > defaultOptions.length + EaglerProfile.skins.size()) { + scrollPos = defaultOptions.length + EaglerProfile.skins.size(); + } + } + } + + private void save() { + EaglerProfile.username = this.username.getTextBoxText().length() == 0 ? "null" : this.username.getTextBoxText(); + mc.session = new Session(EaglerProfile.username, "-"); + EaglerProfile.presetSkinId = selectedSlot - EaglerProfile.skins.size(); + if(EaglerProfile.presetSkinId < 0) { + EaglerProfile.presetSkinId = -1; + EaglerProfile.customSkinId = selectedSlot; + }else { + EaglerProfile.customSkinId = -1; + } + + LocalStorageManager.profileSettingsStorage.setInteger("ps", EaglerProfile.presetSkinId); + LocalStorageManager.profileSettingsStorage.setInteger("cs", EaglerProfile.customSkinId); + LocalStorageManager.profileSettingsStorage.setString("name", EaglerProfile.username); + + NBTTagCompound skins = new NBTTagCompound(); + for(int i = 0, l = EaglerProfile.skins.size(); i < l; i++) { + skins.setByteArray(EaglerProfile.skins.get(i).name, EaglerProfile.skins.get(i).data); + } + LocalStorageManager.profileSettingsStorage.setCompoundTag("skins", skins); + + LocalStorageManager.saveStorageP(); + } + + protected void actionPerformed(GuiButton par1GuiButton) { + if(!dropDownOpen) { + if(par1GuiButton.id == 200) { + save(); + this.mc.displayGuiScreen((GuiScreen) parent); + }else if(par1GuiButton.id == 2) { + EaglerAdapter.openFileChooser("png", "image/png"); + }else if(par1GuiButton.id == 3) { + for(EaglerProfileSkin i : EaglerProfile.skins) { + this.mc.renderEngine.deleteTexture(i.glTex); + } + EaglerProfile.skins.clear(); + this.dropDownOptions = defaultOptions; + this.selectedSlot = 0; + save(); + } + } + } + + public void updateScreen() { + this.username.onUpdate(); + + if(dropDownOpen) { + if(EaglerAdapter.mouseIsButtonDown(0)) { + int skinX = this.width / 2 - 20; + int skinY = this.height / 6 + 103; + int skinWidth = 140; + if(mousex >= (skinX + skinWidth - 10) && mousex < (skinX + skinWidth) && mousey >= skinY && mousey < (skinY + skinsHeight)) { + dragging = true; + } + if(dragging) { + int scrollerSize = skinsHeight * slotsVisible / dropDownOptions.length; + scrollPos = (mousey - skinY - (scrollerSize / 2)) * dropDownOptions.length / skinsHeight; + } + }else { + dragging = false; + } + }else { + dragging = false; + } + + byte[] b; + if((b = EaglerAdapter.getFileChooserResult()) != null && b.length > 0) { + EaglerImage img = EaglerAdapter.loadPNG(b); + if(!((img.w == 64 && img.h == 32) || (img.w == 64 && img.h == 64) || (img.w == 128 && img.h == 64) || (img.w == 128 && img.h == 128))) return; + byte[] rawSkin = new byte[img.data.length * 4]; + for(int i = 0; i < img.data.length; i++) { + int i2 = i * 4; int i3 = img.data[i]; + rawSkin[i2] = (byte)(i3 >> 16); + rawSkin[i2 + 1] = (byte)(i3 >> 8); + rawSkin[i2 + 2] = (byte)(i3); + rawSkin[i2 + 3] = (byte)(i3 >> 24); + } + String name = EaglerAdapter.getFileChooserResultName(); + if(name.length() > 32) { + name = name.substring(0, 32); + } + int k; + if((k = EaglerProfile.addSkin(name, rawSkin, false)) != -1) { + selectedSlot = k; + reconcatDD(); + save(); + } + } + } + + public void onGuiClosed() { + EaglerAdapter.enableRepeatEvents(false); + } + + + protected void keyTyped(char par1, int par2) { + this.username.handleKeyboardInput(par1, par2); + + String text = username.getTextBoxText(); + if(text.length() > 16) text = text.substring(0, 16); + text = text.replaceAll("[^A-Za-z0-9\\-_]", "_"); + this.username.setTextBoxText(text); + + if(par2 == 200 && selectedSlot > 0) { + --selectedSlot; + scrollPos = selectedSlot - 2; + } + if(par2 == 208 && selectedSlot < (dropDownOptions.length - 1)) { + ++selectedSlot; + scrollPos = selectedSlot - 2; + } + } + + protected void mouseClicked(int par1, int par2, int par3) { + super.mouseClicked(par1, par2, par3); + this.username.handleMouseInput(par1, par2, par3); + + if (par3 == 0) { + int skinX = this.width / 2 + 140 - 40; + int skinY = this.height / 6 + 82; + + if(par1 >= skinX && par1 < (skinX + 20) && par2 >= skinY && par2 < (skinY + 22)) { + dropDownOpen = !dropDownOpen; + } + + skinX = this.width / 2 - 20; + skinY = this.height / 6 + 82; + int skinWidth = 140; + int skinHeight = skinsHeight; + + if(!(par1 >= skinX && par1 < (skinX + skinWidth) && par2 >= skinY && par2 < (skinY + skinHeight + 22))) { + dropDownOpen = false; + dragging = false; + } + + skinY += 21; + + if(dropDownOpen && !dragging) { + for(int i = 0; i < slotsVisible; i++) { + if(i + scrollPos < dropDownOptions.length) { + if(selectedSlot != i + scrollPos) { + if(par1 >= skinX && par1 < (skinX + skinWidth - 10) && par2 >= (skinY + i*10 + 5) && par2 < (skinY + i*10 + 15) && selectedSlot != i + scrollPos) { + selectedSlot = i + scrollPos; + dropDownOpen = false; + dragging = false; + } + } + } + } + } + + } + } + + + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/TextureLocation.java b/src/main/java/net/lax1dude/eaglercraft/TextureLocation.java index a04ee9b..c8481bc 100644 --- a/src/main/java/net/lax1dude/eaglercraft/TextureLocation.java +++ b/src/main/java/net/lax1dude/eaglercraft/TextureLocation.java @@ -22,7 +22,7 @@ public class TextureLocation { } } - public void bindTexture() { + public int getTexturePointer() { RenderEngine r = Minecraft.getMinecraft().renderEngine; if (glObject == -1) { glObject = r.getTexture(path); @@ -30,7 +30,15 @@ public class TextureLocation { System.err.println("could not load: " + path); } } - r.bindTexture(glObject); + return glObject; + } + + public void bindTexture() { + RenderEngine r = Minecraft.getMinecraft().renderEngine; + int i = getTexturePointer(); + if(i != -1) { + r.bindTexture(i); + } } private static final ArrayList locations = new ArrayList(); diff --git a/src/main/java/net/lax1dude/eaglercraft/WebsocketNetworkManager.java b/src/main/java/net/lax1dude/eaglercraft/WebsocketNetworkManager.java index c62545b..9deff54 100644 --- a/src/main/java/net/lax1dude/eaglercraft/WebsocketNetworkManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/WebsocketNetworkManager.java @@ -140,4 +140,8 @@ public class WebsocketNetworkManager { return this.serverURI; } + public boolean isSocketOpen() { + return EaglerAdapter.connectionOpen(); + } + } diff --git a/src/main/java/net/lax1dude/eaglercraft/beta/EaglercraftSaveManager.java b/src/main/java/net/lax1dude/eaglercraft/beta/EaglercraftSaveManager.java index f011170..8b4d54a 100644 --- a/src/main/java/net/lax1dude/eaglercraft/beta/EaglercraftSaveManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/beta/EaglercraftSaveManager.java @@ -39,7 +39,8 @@ public class EaglercraftSaveManager implements ISaveFormat { } @Override - public List getWorldList() { + public List getWorldList(IProgressUpdate progress) { + progress.displayLoadingString("Loading Worlds...", "just wait a moment"); ArrayList lst = new ArrayList<>(); EaglerAdapter.listFilesAndDirectories(directory).forEach(new Consumer() { @Override diff --git a/src/main/java/net/minecraft/client/Minecraft.java b/src/main/java/net/minecraft/client/Minecraft.java index c31fb69..ae63fcc 100644 --- a/src/main/java/net/minecraft/client/Minecraft.java +++ b/src/main/java/net/minecraft/client/Minecraft.java @@ -5,7 +5,9 @@ package net.minecraft.client; import net.lax1dude.eaglercraft.EaglerAdapter; +import net.lax1dude.eaglercraft.EaglerProfile; import net.lax1dude.eaglercraft.GuiMultiplayer; +import net.lax1dude.eaglercraft.GuiScreenEditProfile; import net.lax1dude.eaglercraft.TextureLocation; import net.lax1dude.eaglercraft.adapter.Tessellator; import net.lax1dude.eaglercraft.beta.EaglercraftSaveManager; @@ -91,15 +93,18 @@ public abstract class Minecraft implements Runnable { EaglerAdapter.glViewport(0, 0, displayWidth, displayHeight); effectRenderer = new EffectRenderer(theWorld, renderEngine); checkGLError("Post startup"); - + + EaglerProfile.loadFromStorage(); + session = new Session(EaglerProfile.username, "-"); + while(EaglerAdapter.keysNext()); while(EaglerAdapter.mouseNext()); ingameGUI = new GuiIngame(this); String srv = EaglerAdapter.getServerToJoinOnLaunch(); if (srv != null && srv.length() > 0) { - displayGuiScreen(new GuiMultiplayer(new GuiMainMenu(), srv)); + displayGuiScreen(new GuiScreenEditProfile(new GuiMultiplayer(new GuiMainMenu(), srv))); } else { - displayGuiScreen(new GuiMainMenu()); + displayGuiScreen(new GuiScreenEditProfile(new GuiMainMenu())); } } @@ -551,8 +556,18 @@ public abstract class Minecraft implements Runnable { playerController.updateController(); if(++holdStillTimer == 150) { if (thePlayer != null) { - ingameGUI.addChatMessage("Note, the game can lag when chunks are generated"); - ingameGUI.addChatMessage("hold still for a few moments and the lag will stop"); + if(isMultiplayerWorld()) { + //ingameGUI.addChatMessage("Known Multiplayer Bugs:"); + //ingameGUI.addChatMessage(" - chunks may not show until you move around"); + //ingameGUI.addChatMessage(" - block crack animation is fucked up"); + }else { + ingameGUI.addChatMessage("Note, the game can lag when chunks are generated"); + ingameGUI.addChatMessage("hold still for a few moments and the lag will stop"); + } + } + }else if(holdStillTimer == 10) { + if(isMultiplayerWorld()) { + renderGlobal.loadRenderers(); // dammit } } } @@ -705,6 +720,7 @@ public abstract class Minecraft implements Runnable { theWorld.difficultySetting = 3; } if (!isWorldLoaded) { + EaglerProfile.freeSkins(); entityRenderer.updateRenderer(); } if (!isWorldLoaded) { @@ -749,7 +765,6 @@ public abstract class Minecraft implements Runnable { } public void startWorld(String s, String s1, long l) { - holdStillTimer = 0; changeWorld1(null); System.gc(); if (field_22008_V.worldNeedsConvert_maybe(s)) { @@ -802,6 +817,7 @@ public abstract class Minecraft implements Runnable { } public void changeWorld2(World world, String s) { + holdStillTimer = 0; changeWorld(world, s, null); } @@ -859,6 +875,7 @@ public abstract class Minecraft implements Runnable { field_22009_h = thePlayer; mouseHelper.grabMouse(); } else { + EaglerProfile.freeAllSkins(); ungrabMouseCursor(); thePlayer = null; } diff --git a/src/main/java/net/minecraft/src/EntityOtherPlayerMP.java b/src/main/java/net/minecraft/src/EntityOtherPlayerMP.java index bebacf1..4efc6e2 100644 --- a/src/main/java/net/minecraft/src/EntityOtherPlayerMP.java +++ b/src/main/java/net/minecraft/src/EntityOtherPlayerMP.java @@ -16,6 +16,7 @@ public class EntityOtherPlayerMP extends EntityPlayer { skinUrl = (new StringBuilder()).append("http://s3.amazonaws.com/MinecraftSkins/").append(s).append(".png") .toString(); } + skinUrl = "MPSkin" + s; noClip = true; field_22062_y = 0.25F; renderDistanceWeight = 10D; diff --git a/src/main/java/net/minecraft/src/EntityPlayer.java b/src/main/java/net/minecraft/src/EntityPlayer.java index 9814003..1d9044c 100644 --- a/src/main/java/net/minecraft/src/EntityPlayer.java +++ b/src/main/java/net/minecraft/src/EntityPlayer.java @@ -28,6 +28,7 @@ public abstract class EntityPlayer extends EntityLiving { field_9351_C = "humanoid"; field_9353_B = 180F; fireResistance = 20; + skinUrl = "SPSkin"; //texture = "/mob/char.png"; } diff --git a/src/main/java/net/minecraft/src/EntityPlayerSP.java b/src/main/java/net/minecraft/src/EntityPlayerSP.java index 9137a69..c7c44b3 100644 --- a/src/main/java/net/minecraft/src/EntityPlayerSP.java +++ b/src/main/java/net/minecraft/src/EntityPlayerSP.java @@ -18,10 +18,6 @@ public class EntityPlayerSP extends EntityPlayer { field_21902_bL = new MouseFilter(); mc = minecraft; dimension = i; - if (session != null && session.username != null && session.username.length() > 0) { - skinUrl = (new StringBuilder()).append("http://s3.amazonaws.com/MinecraftSkins/").append(session.username) - .append(".png").toString(); - } username = session.username; } diff --git a/src/main/java/net/minecraft/src/EntityRenderer.java b/src/main/java/net/minecraft/src/EntityRenderer.java index d98d40e..a789d98 100644 --- a/src/main/java/net/minecraft/src/EntityRenderer.java +++ b/src/main/java/net/minecraft/src/EntityRenderer.java @@ -349,7 +349,9 @@ public class EntityRenderer { f3 = field_22235_l.func_22386_a(f3, 0.05F * f2); f4 = field_22234_m.func_22386_a(f4, 0.05F * f2); } - mc.thePlayer.func_346_d(f3, f4 * (float) l); + if(mc.thePlayer != null) { + mc.thePlayer.func_346_d(f3, f4 * (float) l); + } } if (mc.field_6307_v) { return; diff --git a/src/main/java/net/minecraft/src/GuiConnecting.java b/src/main/java/net/minecraft/src/GuiConnecting.java index dc41164..13af604 100644 --- a/src/main/java/net/minecraft/src/GuiConnecting.java +++ b/src/main/java/net/minecraft/src/GuiConnecting.java @@ -4,6 +4,7 @@ package net.minecraft.src; import java.io.IOException; import net.lax1dude.eaglercraft.EaglerAdapter; +import net.lax1dude.eaglercraft.EaglerProfile; // Jad home page: http://www.kpdus.com/jad.html // Decompiler options: packimports(3) braces deadcode @@ -61,6 +62,7 @@ public class GuiConnecting extends GuiScreen { this.clientHandler = new NetClientHandler(mc, uri, 0); this.clientHandler.addToSendQueue(new Packet2Handshake(mc.session.username)); + this.clientHandler.addToSendQueue(new Packet69EaglercraftData("EAG|MySkin", EaglerProfile.getSelfSkinPacket())); } catch (IOException e) { try { this.clientHandler.disconnect(); diff --git a/src/main/java/net/minecraft/src/GuiMainMenu.java b/src/main/java/net/minecraft/src/GuiMainMenu.java index 5f4c2f3..e75df31 100644 --- a/src/main/java/net/minecraft/src/GuiMainMenu.java +++ b/src/main/java/net/minecraft/src/GuiMainMenu.java @@ -9,6 +9,7 @@ import net.lax1dude.eaglercraft.ConfigConstants; import net.lax1dude.eaglercraft.EaglerAdapter; import net.lax1dude.eaglercraft.EaglercraftRandom; import net.lax1dude.eaglercraft.GuiMultiplayer; +import net.lax1dude.eaglercraft.GuiScreenEditProfile; import net.lax1dude.eaglercraft.TextureLocation; import net.lax1dude.eaglercraft.adapter.Tessellator; import net.lax1dude.eaglercraft.beta.GuiNoMultiplayer; @@ -72,8 +73,8 @@ public class GuiMainMenu extends GuiScreen { // mc.displayGuiScreen(new GuiTexturePacks(this)); //} if (guibutton.id == 4) { - mc.displayGuiScreen(new GuiNoMultiplayer(this)); - //mc.displayGuiScreen(new GuiMultiplayer(this)); + //mc.displayGuiScreen(new GuiNoMultiplayer(this)); + mc.displayGuiScreen(new GuiScreenEditProfile(this)); } } @@ -98,6 +99,7 @@ public class GuiMainMenu extends GuiScreen { drawString(fontRenderer, s, width - fontRenderer.getStringWidth(s) - 2, height - 10, 0xffffff); drawString(fontRenderer, ConfigConstants.mainMenuString, 2, height - 10, 0xffffff); + /* EaglerAdapter.glPushMatrix(); float ff = 0.75f; EaglerAdapter.glScalef(ff, ff, ff); @@ -107,6 +109,7 @@ public class GuiMainMenu extends GuiScreen { drawString(fontRenderer, str, (int)(((width / ff) - w) / 2), (int)((height / 4 + 102) / ff), 0xffeeee); EaglerAdapter.glPopMatrix(); + */ super.drawScreen(i, j, f); } diff --git a/src/main/java/net/minecraft/src/GuiSelectWorld.java b/src/main/java/net/minecraft/src/GuiSelectWorld.java index b5d19e1..ea4d34c 100644 --- a/src/main/java/net/minecraft/src/GuiSelectWorld.java +++ b/src/main/java/net/minecraft/src/GuiSelectWorld.java @@ -35,7 +35,7 @@ public class GuiSelectWorld extends GuiScreen { private void func_22084_k() { ISaveFormat isaveformat = mc.func_22004_c(); - field_22100_m = isaveformat.getWorldList(); + field_22100_m = isaveformat.getWorldList(mc.loadingScreen); Collections.sort(field_22100_m); field_22101_l = -1; } diff --git a/src/main/java/net/minecraft/src/ISaveFormat.java b/src/main/java/net/minecraft/src/ISaveFormat.java index 324fe93..441f280 100644 --- a/src/main/java/net/minecraft/src/ISaveFormat.java +++ b/src/main/java/net/minecraft/src/ISaveFormat.java @@ -12,7 +12,7 @@ public interface ISaveFormat { public abstract ISaveHandler loadWorldHandler(String s, boolean flag); - public abstract List getWorldList(); + public abstract List getWorldList(IProgressUpdate progress); public abstract void flushCache(); diff --git a/src/main/java/net/minecraft/src/ItemRenderer.java b/src/main/java/net/minecraft/src/ItemRenderer.java index 0b38a55..3b3ebab 100644 --- a/src/main/java/net/minecraft/src/ItemRenderer.java +++ b/src/main/java/net/minecraft/src/ItemRenderer.java @@ -2,6 +2,7 @@ package net.minecraft.src; // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov. import net.lax1dude.eaglercraft.EaglerAdapter; +import net.lax1dude.eaglercraft.EaglerProfile; import net.lax1dude.eaglercraft.TextureLocation; import net.lax1dude.eaglercraft.adapter.Tessellator; @@ -185,8 +186,13 @@ public class ItemRenderer { f10 = MathHelper.sin(MathHelper.sqrt_float(f6) * 3.141593F); EaglerAdapter.glRotatef(f10 * 70F, 0.0F, 1.0F, 0.0F); EaglerAdapter.glRotatef(-f8 * 20F, 0.0F, 0.0F, 1.0F); - EaglerAdapter.glBindTexture(3553 /* GL_TEXTURE_2D */, mc.renderEngine - .getTextureForDownloadableImage(mc.thePlayer.skinUrl, mc.thePlayer.getEntityTexture())); +// EaglerAdapter.glBindTexture(3553 /* GL_TEXTURE_2D */, mc.renderEngine +// .getTextureForDownloadableImage(mc.thePlayer.skinUrl, mc.thePlayer.getEntityTexture())); + if(EaglerProfile.presetSkinId < 0) { + mc.renderEngine.bindTexture(EaglerProfile.skins.get(EaglerProfile.customSkinId).glTex); + }else { + EaglerProfile.defaultOptionsTextures[EaglerProfile.presetSkinId].bindTexture(); + } EaglerAdapter.glTranslatef(-1F, 3.6F, 3.5F); EaglerAdapter.glRotatef(120F, 0.0F, 0.0F, 1.0F); EaglerAdapter.glRotatef(200F, 1.0F, 0.0F, 0.0F); diff --git a/src/main/java/net/minecraft/src/ModelBiped.java b/src/main/java/net/minecraft/src/ModelBiped.java index d1d0554..17a3e37 100644 --- a/src/main/java/net/minecraft/src/ModelBiped.java +++ b/src/main/java/net/minecraft/src/ModelBiped.java @@ -1,4 +1,7 @@ package net.minecraft.src; + +import net.lax1dude.eaglercraft.EaglerAdapter; + // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov. // Jad home page: http://www.kpdus.com/jad.html @@ -50,7 +53,17 @@ public class ModelBiped extends ModelBase { public void render(float f, float f1, float f2, float f3, float f4, float f5) { setRotationAngles(f, f1, f2, f3, f4, f5); bipedHead.render(f5); + + if(blockTransparentSkins) { + EaglerAdapter.glDisable(EaglerAdapter.GL_ALPHA_TEST); + } + bipedBody.render(f5); + + if(blockTransparentSkins) { + EaglerAdapter.glEnable(EaglerAdapter.GL_ALPHA_TEST); + } + bipedRightArm.render(f5); bipedLeftArm.render(f5); bipedRightLeg.render(f5); @@ -156,4 +169,6 @@ public class ModelBiped extends ModelBase { public boolean field_1279_h; public boolean field_1278_i; public boolean isSneak; + public boolean blockTransparentSkins = false; + } diff --git a/src/main/java/net/minecraft/src/NBTTagCompound.java b/src/main/java/net/minecraft/src/NBTTagCompound.java index 69e4e0a..7421bdf 100644 --- a/src/main/java/net/minecraft/src/NBTTagCompound.java +++ b/src/main/java/net/minecraft/src/NBTTagCompound.java @@ -175,4 +175,12 @@ public class NBTTagCompound extends NBTBase { } private Map tagMap; + + public NBTBase getTag(String s) { + return (NBTBase) tagMap.get(s); + } + + public static Map getTagMap(NBTTagCompound nb) { + return nb.tagMap; + } } diff --git a/src/main/java/net/minecraft/src/NetClientHandler.java b/src/main/java/net/minecraft/src/NetClientHandler.java index 5f00b4a..a6b3fc1 100644 --- a/src/main/java/net/minecraft/src/NetClientHandler.java +++ b/src/main/java/net/minecraft/src/NetClientHandler.java @@ -6,6 +6,7 @@ package net.minecraft.src; import java.io.*; +import net.lax1dude.eaglercraft.EaglerProfile; import net.lax1dude.eaglercraft.EaglercraftRandom; import net.lax1dude.eaglercraft.GuiMultiplayer; import net.lax1dude.eaglercraft.WebsocketNetworkManager; @@ -23,9 +24,14 @@ public class NetClientHandler extends NetHandler { public void processReadPackets() { if (disconnected) { + if(mc.theWorld != null) { + mc.changeWorld1(null); + mc.displayGuiScreen(new GuiConnectFailed("disconnect.disconnected", "disconnect.endOfStream", new Object[0])); + } return; } else { netManager.processReadPackets(); + disconnected = !netManager.isSocketOpen(); return; } } @@ -336,14 +342,19 @@ public class NetClientHandler extends NetHandler { } public void handleHandshake(Packet2Handshake packet2handshake) { - if(packet2handshake.username.length() < 26 || mc.gameSettings.lastPasswordLength <= 0) { + if(packet2handshake.username.length() < 24 || mc.gameSettings.lastPasswordLength <= 0) { addToSendQueue(new Packet1Login(mc.session.username, "NULL", 9)); }else { - String hsh = GuiMultiplayer.makeLoginHash(mc.gameSettings.lastPasswordHash, packet2handshake.username); + String hsh = (mc.gameSettings.lastPasswordHash == null || mc.gameSettings.lastPasswordHash.length() == 0 || mc.gameSettings.lastPasswordHash.equalsIgnoreCase("null")) ? + null : GuiMultiplayer.makeLoginHash(mc.gameSettings.lastPasswordHash, packet2handshake.username); if(hsh != null) { addToSendQueue(new Packet1Login(mc.session.username, hsh, 9)); }else { - addToSendQueue(new Packet1Login(mc.session.username, "NULL", 9)); + disconnected = true; + netManager.networkShutdown("disconnect.closed", new Object[0]); + mc.changeWorld1(null); + mc.displayGuiScreen(new GuiConnectFailed("disconnect.disconnected", "disconnect.genericReason", + new Object[] { "A password is required to join this server!" })); } } } @@ -522,6 +533,12 @@ public class NetClientHandler extends NetHandler { packet54.pitch); } + public void handleEaglercraftData(Packet69EaglercraftData packet) { + if(packet.type.equals("EAG|PlayerSkin")) { + EaglerProfile.processSkinResponse(packet.data); + } + } + private boolean disconnected; private WebsocketNetworkManager netManager; public String field_1209_a; diff --git a/src/main/java/net/minecraft/src/NetHandler.java b/src/main/java/net/minecraft/src/NetHandler.java index b8d43f4..a2df1e7 100644 --- a/src/main/java/net/minecraft/src/NetHandler.java +++ b/src/main/java/net/minecraft/src/NetHandler.java @@ -191,4 +191,8 @@ public class NetHandler { public void func_22185_a(Packet27 packet27) { } + + public void handleEaglercraftData(Packet69EaglercraftData packet) { + registerPacket(packet); + } } diff --git a/src/main/java/net/minecraft/src/Packet.java b/src/main/java/net/minecraft/src/Packet.java index b96ebd2..ad794b1 100644 --- a/src/main/java/net/minecraft/src/Packet.java +++ b/src/main/java/net/minecraft/src/Packet.java @@ -151,6 +151,7 @@ public abstract class Packet { addIdClassMapping(53, Packet53BlockChange.class); addIdClassMapping(54, Packet54.class); addIdClassMapping(60, Packet60.class); + addIdClassMapping(69, Packet69EaglercraftData.class); addIdClassMapping(100, Packet100.class); addIdClassMapping(101, Packet101.class); addIdClassMapping(102, Packet102.class); diff --git a/src/main/java/net/minecraft/src/Packet69EaglercraftData.java b/src/main/java/net/minecraft/src/Packet69EaglercraftData.java new file mode 100644 index 0000000..2004f90 --- /dev/null +++ b/src/main/java/net/minecraft/src/Packet69EaglercraftData.java @@ -0,0 +1,47 @@ +package net.minecraft.src; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public class Packet69EaglercraftData extends Packet { + + public String type; + public byte[] data; + + public Packet69EaglercraftData() { + } + + public Packet69EaglercraftData(String type, byte[] data) { + if(data.length > 65535) { + throw new IllegalArgumentException("Packet69EaglercraftData may at most carry a 65535 byte payload"); + } + this.type = type; + this.data = data; + } + + @Override + public void readPacketData(DataInputStream datainputstream) throws IOException { + type = datainputstream.readUTF(); + data = new byte[datainputstream.readUnsignedShort()]; + datainputstream.read(data); + } + + @Override + public void writePacketData(DataOutputStream dataoutputstream) throws IOException { + dataoutputstream.writeUTF(type); + dataoutputstream.writeShort(data.length); + dataoutputstream.write(data); + } + + @Override + public void processPacket(NetHandler nethandler) { + nethandler.handleEaglercraftData(this); + } + + @Override + public int getPacketSize() { + return 2 + type.length() + 2 + data.length; + } + +} diff --git a/src/main/java/net/minecraft/src/RenderEngine.java b/src/main/java/net/minecraft/src/RenderEngine.java index 9f8399d..6bef9f0 100644 --- a/src/main/java/net/minecraft/src/RenderEngine.java +++ b/src/main/java/net/minecraft/src/RenderEngine.java @@ -78,9 +78,24 @@ public class RenderEngine { textureNameToImageMap.put(Integer.valueOf(i), bufferedimage); return i; } + + public int allocateAndSetupTexture(byte[] data, int w, int h) { + int i = EaglerAdapter.glGenTextures(); + bindTexture(i); + EaglerAdapter.glTexParameteri(3553 /* GL_TEXTURE_2D */, 10241 /* GL_TEXTURE_MIN_FILTER */, 9729 /* GL_LINEAR */); + EaglerAdapter.glTexParameteri(3553 /* GL_TEXTURE_2D */, 10240 /* GL_TEXTURE_MAG_FILTER */, 9728 /* GL_NEAREST */); + EaglerAdapter.glTexParameteri(3553 /* GL_TEXTURE_2D */, 10242 /* GL_TEXTURE_WRAP_S */, 10497 /* GL_REPEAT */); + EaglerAdapter.glTexParameteri(3553 /* GL_TEXTURE_2D */, 10243 /* GL_TEXTURE_WRAP_T */, 10497 /* GL_REPEAT */); + imageDataB1.clear(); + imageDataB1.put(data); + imageDataB1.position(0).limit(data.length); + EaglerAdapter.glTexImage2D(3553 /* GL_TEXTURE_2D */, 0, 6408 /* GL_RGBA */, w, h, 0, 6408 /* GL_RGBA */, + 5121 /* GL_UNSIGNED_BYTE */, imageDataB1); + return i; + } public void setupTexture(EaglerImage bufferedimage, int i) { - EaglerAdapter.glBindTexture(3553 /* GL_TEXTURE_2D */, i); + bindTexture(i); if (useMipmaps) { EaglerAdapter.glTexParameteri(3553 /* GL_TEXTURE_2D */, 10241 /* GL_TEXTURE_MIN_FILTER */, EaglerAdapter.GL_NEAREST_MIPMAP_LINEAR); EaglerAdapter.glTexParameteri(3553 /* GL_TEXTURE_2D */, 10240 /* GL_TEXTURE_MAG_FILTER */, EaglerAdapter.GL_NEAREST /* GL_LINEAR */); @@ -343,11 +358,11 @@ public class RenderEngine { TextureFX texturefx = (TextureFX) textureList.get(i); texturefx.anaglyphEnabled = options.anaglyph; texturefx.onTick(); + texturefx.bindImage(this); int tileSize = 16 * 16 * 4; imageDataB1.clear(); imageDataB1.put(texturefx.imageData); imageDataB1.position(0).limit(tileSize); - texturefx.bindImage(this); EaglerAdapter.glTexSubImage2D(3553 /* GL_TEXTURE_2D */, 0, (texturefx.iconIndex % 16) * 16, (texturefx.iconIndex / 16) * 16, 16, 16, 6408 /* GL_RGBA */, 5121 /* GL_UNSIGNED_BYTE */, imageDataB1); } diff --git a/src/main/java/net/minecraft/src/RenderFallingSand.java b/src/main/java/net/minecraft/src/RenderFallingSand.java index 862a18e..1cea601 100644 --- a/src/main/java/net/minecraft/src/RenderFallingSand.java +++ b/src/main/java/net/minecraft/src/RenderFallingSand.java @@ -25,6 +25,7 @@ public class RenderFallingSand extends Render { Block block = Block.blocksList[entityfallingsand.blockID]; World world = entityfallingsand.func_465_i(); EaglerAdapter.glDisable(2896 /* GL_LIGHTING */); + EaglerAdapter.glColor4f(1f, 1f, 1f, 1f); field_197_d.renderBlockFallingSand(block, world, MathHelper.floor_double(entityfallingsand.posX), MathHelper.floor_double(entityfallingsand.posY), MathHelper.floor_double(entityfallingsand.posZ)); EaglerAdapter.glEnable(2896 /* GL_LIGHTING */); diff --git a/src/main/java/net/minecraft/src/RenderGlobal.java b/src/main/java/net/minecraft/src/RenderGlobal.java index acabc86..0e2fd2d 100644 --- a/src/main/java/net/minecraft/src/RenderGlobal.java +++ b/src/main/java/net/minecraft/src/RenderGlobal.java @@ -835,33 +835,28 @@ public class RenderGlobal implements IWorldAccess { } public boolean updateRenderers(EntityLiving entityliving, boolean flag) { - boolean flag1 = false; - if (flag1) { - Collections.sort(worldRenderersToUpdate, new RenderSorter(entityliving)); - int i = worldRenderersToUpdate.size() - 1; - int j = worldRenderersToUpdate.size(); - for (int k = 0; k < j; k++) { - WorldRenderer worldrenderer = (WorldRenderer) worldRenderersToUpdate.get(i - k); - if (!flag) { - if (worldrenderer.distanceToEntity(entityliving) > 1024F) { - if (worldrenderer.isInFrustum) { - if (k >= 3) { - return false; - } - } else if (k >= 1) { - return false; - } + //boolean flag1 = false; + //if (flag1) { + int t = worldRenderersToUpdate.size(); + if(t > 0) { + Collections.sort(worldRenderersToUpdate, new RenderSorter(entityliving)); + boolean b = false; + for(int i = t - 1; i >= 0; --i) { + WorldRenderer worldrenderer = (WorldRenderer) worldRenderersToUpdate.get(i); + if(worldrenderer.isInFrustum || worldrenderer.distanceToEntity(entityliving) < 1024F) { + b = true; + worldrenderer.updateRenderer(); + worldRenderersToUpdate.remove(i); + break; } - } else if (!worldrenderer.isInFrustum) { - continue; } - worldrenderer.updateRenderer(); - worldRenderersToUpdate.remove(worldrenderer); - worldrenderer.needsUpdate = false; + if(!b) { + ((WorldRenderer)worldRenderersToUpdate.remove(t - 1)).updateRenderer(); + } } - return worldRenderersToUpdate.size() == 0; - } + //} + /* RenderSorter rendersorter = new RenderSorter(entityliving); WorldRenderer aworldrenderer[] = new WorldRenderer[3]; ArrayList arraylist = null; @@ -941,6 +936,7 @@ public class RenderGlobal implements IWorldAccess { worldRenderersToUpdate.remove(j2); } return l == i1 + l1; + */ } private static final TextureLocation terrainTexture = new TextureLocation("/terrain.png"); diff --git a/src/main/java/net/minecraft/src/RenderPlayer.java b/src/main/java/net/minecraft/src/RenderPlayer.java index 67b71e9..d3c5dad 100644 --- a/src/main/java/net/minecraft/src/RenderPlayer.java +++ b/src/main/java/net/minecraft/src/RenderPlayer.java @@ -1,7 +1,14 @@ package net.minecraft.src; // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov. +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; + import net.lax1dude.eaglercraft.EaglerAdapter; +import net.lax1dude.eaglercraft.EaglerProfile; +import net.lax1dude.eaglercraft.EaglerProfile.EnumSkinType; +import net.lax1dude.eaglercraft.EaglerProfile.UserSkin; import net.lax1dude.eaglercraft.TextureLocation; import net.lax1dude.eaglercraft.adapter.Tessellator; @@ -25,6 +32,7 @@ public class RenderPlayer extends RenderLiving { public RenderPlayer() { super(new ModelBiped(0.0F), 0.5F); modelBipedMain = (ModelBiped) mainModel; + modelBipedMain.blockTransparentSkins = true; modelArmorChestplate = new ModelBiped(1.0F); modelArmor = new ModelBiped(0.5F); } @@ -298,7 +306,47 @@ public class RenderPlayer extends RenderLiving { @Override protected boolean loadDownloadableImageTexture(String s, String s1) { - defaultPlayerSkin.bindTexture(); + RenderEngine re = Minecraft.getMinecraft().renderEngine; + if(s == null) { + defaultPlayerSkin.bindTexture(); + }else if(s.equals("SPSkin")) { + if(EaglerProfile.presetSkinId < 0) { + re.bindTexture(EaglerProfile.skins.get(EaglerProfile.customSkinId).glTex); + }else { + EaglerProfile.defaultOptionsTextures[EaglerProfile.presetSkinId].bindTexture(); + } + }else if(s.startsWith("MPSkin")) { + String un = s.substring(6); + UserSkin us = EaglerProfile.getUserSkin(un); + if(us == null) { + if(!EaglerProfile.skinRequestPending(un)) { + World w = Minecraft.getMinecraft().theWorld; + if(w != null && (w instanceof WorldClient)) { + try { + ByteArrayOutputStream bao = new ByteArrayOutputStream(); + DataOutputStream dao = new DataOutputStream(bao); + dao.writeShort(EaglerProfile.beginSkinRequest(un)); + dao.writeUTF(un); + ((WorldClient)w).sendPacket(new Packet69EaglercraftData("EAG|RequestPlayerSkin", bao.toByteArray())); + }catch(IOException exx) { + // ? + } + } + } + defaultPlayerSkin.bindTexture(); + }else { + EnumSkinType st = us.getSkinType(); + if(st == EnumSkinType.PRESET) { + EaglerProfile.defaultOptionsTextures[us.getSkin()].bindTexture(); + }else if(st == EnumSkinType.CUSTOM_LEGACY){ + re.bindTexture(us.getTexture()); + }else { + defaultPlayerSkin.bindTexture(); + } + } + }else { + defaultPlayerSkin.bindTexture(); + } return true; } diff --git a/src/main/java/net/minecraft/src/WorldClient.java b/src/main/java/net/minecraft/src/WorldClient.java index b15399e..246e643 100644 --- a/src/main/java/net/minecraft/src/WorldClient.java +++ b/src/main/java/net/minecraft/src/WorldClient.java @@ -6,6 +6,8 @@ package net.minecraft.src; import java.util.*; +import net.lax1dude.eaglercraft.EaglerProfile; + public class WorldClient extends World { public WorldClient(NetClientHandler netclienthandler, long l, int i) { @@ -102,6 +104,9 @@ public class WorldClient extends World { } public void setEntityDead(Entity entity) { + if(entity instanceof EntityOtherPlayerMP) { + EaglerProfile.freeUserSkin(((EntityOtherPlayerMP)entity).username); + } super.setEntityDead(entity); field_20914_E.remove(entity); } @@ -192,6 +197,10 @@ public class WorldClient extends World { public void sendQuittingDisconnectingPacket() { sendQueue.addToSendQueue(new Packet255KickDisconnect("Quitting")); } + + public void sendPacket(Packet p) { + sendQueue.addToSendQueue(p); + } private LinkedList field_1057_z; private NetClientHandler sendQueue; diff --git a/src/teavm/java/net/lax1dude/eaglercraft/Client.java b/src/teavm/java/net/lax1dude/eaglercraft/Client.java index 1d70325..f62cf90 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/Client.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/Client.java @@ -36,13 +36,20 @@ public class Client { registerErrorHandler(); String[] e = getOpts(); try { - EaglerAdapterImpl2.initializeContext(rootElement = Window.current().getDocument().getElementById(e[0]), e[1]); - }catch(AbortedLaunchException ex) { + try { + EaglerAdapterImpl2.initializeContext(rootElement = Window.current().getDocument().getElementById(e[0]), e[1]); + }catch(AbortedLaunchException ex) { + return; + } + }catch(Throwable ex2) { + StringWriter s = new StringWriter(); + ex2.printStackTrace(new PrintWriter(s)); + showCrashScreen(s.toString()); return; } LocalStorageManager.loadStorage(); if(e.length > 2) { - EaglerAdapterImpl2.setServerToJoinOnLaunch(e[3]); + EaglerAdapterImpl2.setServerToJoinOnLaunch(e[2]); } run0(); }