22w14a (FREE) Fixed 9 bugs and added 3 new features for online server lists

Fixes:
 - server-icon.png can now be any size
 - fixed (0 remaining) in MOTD
 - fixed key event
 - fixed opaque pixels in armor
 - fixed mipmap layout
 - fixed font map interpolation bleeding
 - fixed enchanting GUI
 - fixed bungee log crash
 - fixed duplicate stat uuid

New Features:
 - added DoS protection
 - added MOTD cache hints
 - added UUID to query responses
This commit is contained in:
LAX1DUDE 2022-04-08 01:32:46 -07:00
parent 3490b84ed4
commit bec1a03fa2
51 changed files with 88981 additions and 87065 deletions

View File

@ -1,6 +1,6 @@
# Eaglercraft # Eaglercraft
**Note:** we will be transitioning away from Eaglercraft being standalone 'free browser minecraft' to Eaglercraft being a bukkit/bungee plugin for servers to allow 'online access' to players who register with a command as their real online-mode Minecraft account on the server. Server owners can still elect to set `online-mode=false` in their server.properties to allow free login but now that's their legal screw up instead of mine ### Note: we will be transitioning away from Eaglercraft being standalone 'free browser minecraft' to Eaglercraft being a bukkit/bungee plugin for servers to allow 'online access' to players who register with a command as their real online-mode Minecraft account on the server. Server owners can still elect to set `online-mode=false` in their server.properties to allow free login but now that's their legal screw up instead of mine
![eaglercraft](https://cdn.discordapp.com/attachments/378764518081429506/932053915061587978/thumbnail2.png) ![eaglercraft](https://cdn.discordapp.com/attachments/378764518081429506/932053915061587978/thumbnail2.png)
@ -10,7 +10,7 @@
(right click the link and press 'Save link as...' to download the file) (right click the link and press 'Save link as...' to download the file)
### Visit this site for a list of servers: [https://g.eags.us/eaglercraft/servers/](https://g.eags.us/eaglercraft/servers/) ### A new server list site is under construction
**For any questions you can join the discord server and hit me up there [https://discord.gg/KMQW9Uvjyq](https://discord.gg/KMQW9Uvjyq)** **For any questions you can join the discord server and hit me up there [https://discord.gg/KMQW9Uvjyq](https://discord.gg/KMQW9Uvjyq)**
@ -76,7 +76,7 @@ Keep both the first and second terminal window you opened, just minimize them do
I successfully created Singleplayer for this version of eaglercraft and it works 100%, **but after many rounds of very thorough testing**, I found that TeaVM is unable to optimize certain aspects of terrain generation and world ticking as well as it can optimize the rendering for a multiplayer-only build. On an [i9-11900K]( https://www.cpubenchmark.net/cpu.php?cpu=Intel+Core+i9-11900K+%40+3.50GHz&id=3904) a render distance of 'Tiny' struggles to pass 12 TPS while standing still, and drops below 1 TPS as soon as a couple new chunks have to be generated and trigger lighting updates. The playerbase of this game **will bother me every f\*\*king day if what I release as singleplayer is not perfect** and therefore singleplayer will remain private indefinetly and I will not answer any further questions about it or share the source code unless you are just looking to repurpose some of the base OS emulation code. I successfully created Singleplayer for this version of eaglercraft and it works 100%, **but after many rounds of very thorough testing**, I found that TeaVM is unable to optimize certain aspects of terrain generation and world ticking as well as it can optimize the rendering for a multiplayer-only build. On an [i9-11900K]( https://www.cpubenchmark.net/cpu.php?cpu=Intel+Core+i9-11900K+%40+3.50GHz&id=3904) a render distance of 'Tiny' struggles to pass 12 TPS while standing still, and drops below 1 TPS as soon as a couple new chunks have to be generated and trigger lighting updates. The playerbase of this game **will bother me every f\*\*king day if what I release as singleplayer is not perfect** and therefore singleplayer will remain private indefinetly and I will not answer any further questions about it or share the source code unless you are just looking to repurpose some of the base OS emulation code.
**Minecraft Alpha singleplayer will be ported in the coming weeks but that is not a promise I should be obligated to fulfill** **Minecraft Beta singleplayer will be ported sometime soon**
## How does it work? ## How does it work?
@ -90,6 +90,29 @@ I got tired of closing duplicate 'how to maek sever' and 'add single player' iss
EaglercraftBungee translates WebSockets to a raw Minecraft 1.5.2 TCP connection. It is just regular BungeeCord with more `config.yml` options, and a built in plugin for syncing people's custom skins between clients so people can see each other's skins EaglercraftBungee translates WebSockets to a raw Minecraft 1.5.2 TCP connection. It is just regular BungeeCord with more `config.yml` options, and a built in plugin for syncing people's custom skins between clients so people can see each other's skins
**Enable** `forward_ip` **and pass a X-Real-IP header from your proxy to use rate limiting and IP bans on a URL**
**The server has built in DoS protection, reset it via typing 'eag-ratelimit reset' in the bungee console**
```yaml
ratelimit:
ip:
enable: true
period: 90
limit: 60
limit_lockout: 80
lockout_duration: 1200
exceptions: []
```
- `enable` enable rate limiting
- `period` and `limit` set the number of requests (`limit`) can be made in (`period`) number of seconds
- `limit_lockout` and `lockout_duration` set the number of requests (`limit_lockout`) that can be made in (`period`) seconds before the IP is blocked for `lockout_duration` number of seconds
- `exceptions` a list of IP addresses that should never get rate limited. **Local IPs like 127.0.0.1 and 192.168.\*.\* and such are set as exceptions by default**
**To develop a plugin, download [stable-download/java/bungee_command/bungee_dist.jar](https://github.com/LAX1DUDE/eaglercraft/blob/main/stable-download/java/bungee_command/bungee-dist.jar) and add it to the Build Path of your Java IDE. Develop the plugin just like a regular BungeeCord plugin, see [EaglerMOTD](https://github.com/LAX1DUDE/eaglercraft-motd/) for an example.** **To develop a plugin, download [stable-download/java/bungee_command/bungee_dist.jar](https://github.com/LAX1DUDE/eaglercraft/blob/main/stable-download/java/bungee_command/bungee-dist.jar) and add it to the Build Path of your Java IDE. Develop the plugin just like a regular BungeeCord plugin, see [EaglerMOTD](https://github.com/LAX1DUDE/eaglercraft-motd/) for an example.**
**Test your plugin by exporting it as a jar and putting it in the '/plugins' directory of EaglercraftBungee and then clicking 'run.bat'** **Test your plugin by exporting it as a jar and putting it in the '/plugins' directory of EaglercraftBungee and then clicking 'run.bat'**

View File

@ -59,6 +59,7 @@ import net.md_5.bungee.command.CommandGlobalUnban;
import net.md_5.bungee.command.CommandSend; import net.md_5.bungee.command.CommandSend;
import net.md_5.bungee.command.CommandPerms; import net.md_5.bungee.command.CommandPerms;
import net.md_5.bungee.command.CommandBungee; import net.md_5.bungee.command.CommandBungee;
import net.md_5.bungee.command.CommandClearRatelimit;
import net.md_5.bungee.command.CommandAlert; import net.md_5.bungee.command.CommandAlert;
import net.md_5.bungee.command.CommandIP; import net.md_5.bungee.command.CommandIP;
import net.md_5.bungee.command.CommandServer; import net.md_5.bungee.command.CommandServer;
@ -152,6 +153,7 @@ public class BungeeCord extends ProxyServer {
this.getPluginManager().registerCommand(null, new CommandPerms()); this.getPluginManager().registerCommand(null, new CommandPerms());
this.getPluginManager().registerCommand(null, new CommandSend()); this.getPluginManager().registerCommand(null, new CommandSend());
this.getPluginManager().registerCommand(null, new CommandFind()); this.getPluginManager().registerCommand(null, new CommandFind());
this.getPluginManager().registerCommand(null, new CommandClearRatelimit());
this.registerChannel("BungeeCord"); this.registerChannel("BungeeCord");
Log.setOutput(new PrintStream(ByteStreams.nullOutputStream())); Log.setOutput(new PrintStream(ByteStreams.nullOutputStream()));
AnsiConsole.systemInstall(); AnsiConsole.systemInstall();
@ -244,6 +246,11 @@ public class BungeeCord extends ProxyServer {
public void run() { public void run() {
for(WebSocketListener lst : BungeeCord.this.wsListeners) { for(WebSocketListener lst : BungeeCord.this.wsListeners) {
lst.closeInactiveSockets(); lst.closeInactiveSockets();
ListenerInfo info = lst.getInfo();
if(info.getRateLimitIP() != null) info.getRateLimitIP().deleteClearLimiters();
if(info.getRateLimitLogin() != null) info.getRateLimitLogin().deleteClearLimiters();
if(info.getRateLimitMOTD() != null) info.getRateLimitMOTD().deleteClearLimiters();
if(info.getRateLimitQuery() != null) info.getRateLimitQuery().deleteClearLimiters();
} }
} }
}, 0L, TimeUnit.SECONDS.toMillis(10L)); }, 0L, TimeUnit.SECONDS.toMillis(10L));

View File

@ -12,6 +12,7 @@ public interface MOTD extends QueryConnection {
public int[] getBitmap(); public int[] getBitmap();
public int getOnlinePlayers(); public int getOnlinePlayers();
public int getMaxPlayers(); public int getMaxPlayers();
public String getSubType();
public void setLine1(String p); public void setLine1(String p);
public void setLine2(String p); public void setLine2(String p);

View File

@ -39,6 +39,7 @@ public interface QueryConnection {
toSend.put("vers", EaglercraftBungee.version); toSend.put("vers", EaglercraftBungee.version);
toSend.put("cracked", EaglercraftBungee.cracked); toSend.put("cracked", EaglercraftBungee.cracked);
toSend.put("time", System.currentTimeMillis()); toSend.put("time", System.currentTimeMillis());
toSend.put("uuid", BungeeCord.getInstance().config.getUuid());
toSend.put("data", msg); toSend.put("data", msg);
writeResponseRaw(toSend.toString()); writeResponseRaw(toSend.toString());
} }
@ -51,6 +52,7 @@ public interface QueryConnection {
toSend.put("vers", EaglercraftBungee.version); toSend.put("vers", EaglercraftBungee.version);
toSend.put("cracked", EaglercraftBungee.cracked); toSend.put("cracked", EaglercraftBungee.cracked);
toSend.put("time", System.currentTimeMillis()); toSend.put("time", System.currentTimeMillis());
toSend.put("uuid", BungeeCord.getInstance().config.getUuid());
toSend.put("data", msg); toSend.put("data", msg);
writeResponseRaw(toSend.toString()); writeResponseRaw(toSend.toString());
} }

View File

@ -1,5 +1,8 @@
package net.md_5.bungee.api; package net.md_5.bungee.api;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
@ -9,10 +12,39 @@ import javax.imageio.ImageIO;
public class ServerIcon { public class ServerIcon {
public static int[] createServerIcon(BufferedImage awtIcon) { public static int[] createServerIcon(BufferedImage awtIcon) {
if(awtIcon.getWidth() != 64 || awtIcon.getHeight() != 64) { BufferedImage icon = awtIcon;
throw new IllegalArgumentException("Image must be 64x64 (was " + awtIcon.getWidth() + "x" + awtIcon.getHeight() + ")"); boolean gotScaled = false;
if(icon.getWidth() != 64 || icon.getHeight() != 64) {
icon = new BufferedImage(64, 64, awtIcon.getType());
Graphics2D g = (Graphics2D) icon.getGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, (awtIcon.getWidth() < 64 || awtIcon.getHeight() < 64) ?
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR : RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g.setBackground(Color.BLACK);
g.clearRect(0, 0, 64, 64);
int ow = awtIcon.getWidth();
int oh = awtIcon.getHeight();
int nw, nh;
float aspectRatio = (float)oh / (float)ow;
if(aspectRatio >= 1.0f) {
nw = (int)(64 / aspectRatio);
nh = 64;
}else {
nw = 64;
nh = (int)(64 * aspectRatio);
}
g.drawImage(awtIcon, (64 - nw) / 2, (64 - nh) / 2, (64 - nw) / 2 + nw, (64 - nh) / 2 + nh, 0, 0, awtIcon.getWidth(), awtIcon.getHeight(), null);
g.dispose();
gotScaled = true;
} }
return awtIcon.getRGB(0, 0, 64, 64, new int[4096], 0, 64); int[] pxls = icon.getRGB(0, 0, 64, 64, new int[4096], 0, 64);
if(gotScaled) {
for(int i = 0; i < pxls.length; ++i) {
if((pxls[i] & 0xFFFFFF) == 0) {
pxls[i] = 0;
}
}
}
return pxls;
} }
public static int[] createServerIcon(InputStream f) { public static int[] createServerIcon(InputStream f) {

View File

@ -25,4 +25,8 @@ public interface ConfigurationAdapter {
Collection<String> getPermissions(final String p0); Collection<String> getPermissions(final String p0);
AuthServiceInfo getAuthSettings(); AuthServiceInfo getAuthSettings();
Map<String, Object> getMap();
void forceSave();
} }

View File

@ -4,15 +4,17 @@
package net.md_5.bungee.api.config; package net.md_5.bungee.api.config;
import java.beans.ConstructorProperties;
import java.io.File; import java.io.File;
import net.md_5.bungee.api.ServerIcon; import net.md_5.bungee.api.ServerIcon;
import net.md_5.bungee.api.tab.TabListHandler; import net.md_5.bungee.api.tab.TabListHandler;
import net.md_5.bungee.eaglercraft.WebSocketRateLimiter;
import java.util.Map; import java.util.Map;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
public class ListenerInfo { public class ListenerInfo {
private final String hostString;
private final InetSocketAddress host; private final InetSocketAddress host;
private final String motd; private final String motd;
private final int maxPlayers; private final int maxPlayers;
@ -29,10 +31,19 @@ public class ListenerInfo {
private final int[] serverIconCache; private final int[] serverIconCache;
private boolean serverIconLoaded; private boolean serverIconLoaded;
private boolean serverIconSet; private boolean serverIconSet;
private final boolean allowMOTD;
private final boolean allowQuery;
private final MOTDCacheConfiguration cacheConfig;
private final WebSocketRateLimiter rateLimitIP;
private final WebSocketRateLimiter rateLimitLogin;
private final WebSocketRateLimiter rateLimitMOTD;
private final WebSocketRateLimiter rateLimitQuery;
@ConstructorProperties({ "host", "motd", "maxPlayers", "tabListSize", "defaultServer", "fallbackServer", "forceDefault", "websocket", "forwardIp", "forcedHosts", "texturePack", "tabList", "serverIcon" }) public ListenerInfo(final String hostString, final InetSocketAddress host, final String motd, final int maxPlayers, final int tabListSize, final String defaultServer, final String fallbackServer, final boolean forceDefault, final boolean websocket,
public ListenerInfo(final InetSocketAddress host, final String motd, final int maxPlayers, final int tabListSize, final String defaultServer, final String fallbackServer, final boolean forceDefault, final boolean websocket, final boolean forwardIp, final Map<String, String> forcedHosts, final TexturePackInfo texturePack, final Class<? extends TabListHandler> tabList, final String serverIcon, final MOTDCacheConfiguration cacheConfig,
final boolean forwardIp, final Map<String, String> forcedHosts, final TexturePackInfo texturePack, final Class<? extends TabListHandler> tabList, final String serverIcon) { final boolean allowMOTD, final boolean allowQuery, final WebSocketRateLimiter rateLimitIP, final WebSocketRateLimiter rateLimitLogin, final WebSocketRateLimiter rateLimitMOTD, final WebSocketRateLimiter rateLimitQuery) {
this.hostString = hostString;
this.host = host; this.host = host;
this.motd = motd; this.motd = motd;
this.maxPlayers = maxPlayers; this.maxPlayers = maxPlayers;
@ -49,6 +60,17 @@ public class ListenerInfo {
this.serverIconCache = new int[4096]; this.serverIconCache = new int[4096];
this.serverIconLoaded = false; this.serverIconLoaded = false;
this.serverIconSet = false; this.serverIconSet = false;
this.allowMOTD = allowMOTD;
this.allowQuery = allowQuery;
this.cacheConfig = cacheConfig;
this.rateLimitIP = rateLimitIP;
this.rateLimitLogin = rateLimitLogin;
this.rateLimitMOTD = rateLimitMOTD;
this.rateLimitQuery = rateLimitQuery;
}
public String getHostString() {
return this.hostString;
} }
public InetSocketAddress getHost() { public InetSocketAddress getHost() {
@ -278,4 +300,45 @@ public class ListenerInfo {
getServerIconCache(); getServerIconCache();
return serverIconSet; return serverIconSet;
} }
public boolean isForwardIp() {
return forwardIp;
}
public boolean isServerIconLoaded() {
return serverIconLoaded;
}
public boolean isServerIconSet() {
return serverIconSet;
}
public boolean isAllowMOTD() {
return allowMOTD;
}
public boolean isAllowQuery() {
return allowQuery;
}
public MOTDCacheConfiguration getCacheConfig() {
return cacheConfig;
}
public WebSocketRateLimiter getRateLimitIP() {
return rateLimitIP;
}
public WebSocketRateLimiter getRateLimitLogin() {
return rateLimitLogin;
}
public WebSocketRateLimiter getRateLimitMOTD() {
return rateLimitMOTD;
}
public WebSocketRateLimiter getRateLimitQuery() {
return rateLimitQuery;
}
} }

View File

@ -0,0 +1,21 @@
package net.md_5.bungee.api.config;
public class MOTDCacheConfiguration {
public final int cacheTTL;
public final boolean cacheServerListAnimation;
public final boolean cacheServerListResults;
public final boolean cacheServerListTrending;
public final boolean cacheServerListPortfolios;
public MOTDCacheConfiguration(int cacheTTL, boolean cacheServerListAnimation, boolean cacheServerListResults,
boolean cacheServerListTrending, boolean cacheServerListPortfolios) {
this.cacheTTL = cacheTTL;
this.cacheServerListAnimation = cacheServerListAnimation;
this.cacheServerListResults = cacheServerListResults;
this.cacheServerListTrending = cacheServerListTrending;
this.cacheServerListPortfolios = cacheServerListPortfolios;
}
}

View File

@ -0,0 +1,118 @@
package net.md_5.bungee.command;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.plugin.Command;
public class CommandClearRatelimit extends Command {
public CommandClearRatelimit() {
super("eag-ratelimit", "bungeecord.command.eag.ratelimit", "e-ratelimit", "gratelimit");
}
@Override
public void execute(CommandSender p0, String[] p1) {
if(p1.length >= 1 && ("clear".equalsIgnoreCase(p1[0]) || "reset".equalsIgnoreCase(p1[0]))) {
if(p1.length == 1 || (p1.length == 2 && "all".equalsIgnoreCase(p1[1]))) {
for(ListenerInfo l : BungeeCord.getInstance().config.getListeners()) {
if(l.getRateLimitIP() != null) l.getRateLimitIP().resetLimiters();
if(l.getRateLimitLogin() != null) l.getRateLimitLogin().resetLimiters();
if(l.getRateLimitMOTD() != null) l.getRateLimitMOTD().resetLimiters();
if(l.getRateLimitQuery() != null) l.getRateLimitQuery().resetLimiters();
}
p0.sendMessage(ChatColor.GREEN + "Reset all ratelimits");
return;
}else if(p1.length == 2 || p1.length == 3) {
ListenerInfo ll = null;
if(p1.length == 3) {
for(ListenerInfo l : BungeeCord.getInstance().config.getListeners()) {
if(l.getHostString().equalsIgnoreCase(p1[2])) {
ll = l;
break;
}
}
if(ll == null) {
p0.sendMessage(ChatColor.RED + "Listener does not exist: " + ChatColor.WHITE + p1[2]);
String accum = "";
for(ListenerInfo l : BungeeCord.getInstance().config.getListeners()) {
if(accum.length() > 0) {
accum += ", ";
}
accum += l.getHostString();
}
p0.sendMessage(ChatColor.GREEN + "Listeners Available: " + ChatColor.WHITE + accum);
return;
}
}
if("all".equalsIgnoreCase(p1[1])) {
if(ll != null) {
if(ll.getRateLimitIP() != null) ll.getRateLimitIP().resetLimiters();
if(ll.getRateLimitLogin() != null) ll.getRateLimitLogin().resetLimiters();
if(ll.getRateLimitMOTD() != null) ll.getRateLimitMOTD().resetLimiters();
if(ll.getRateLimitQuery() != null) ll.getRateLimitQuery().resetLimiters();
p0.sendMessage(ChatColor.GREEN + "Reset all ratelimits on listener: " + ChatColor.WHITE + ll.getHostString());
}else {
for(ListenerInfo l : BungeeCord.getInstance().config.getListeners()) {
if(l.getRateLimitIP() != null) l.getRateLimitIP().resetLimiters();
if(l.getRateLimitLogin() != null) l.getRateLimitLogin().resetLimiters();
if(l.getRateLimitMOTD() != null) l.getRateLimitMOTD().resetLimiters();
if(l.getRateLimitQuery() != null) l.getRateLimitQuery().resetLimiters();
}
p0.sendMessage(ChatColor.GREEN + "Reset all ratelimits");
}
return;
}else if("ip".equalsIgnoreCase(p1[1])) {
if(ll != null) {
if(ll.getRateLimitIP() != null) ll.getRateLimitIP().resetLimiters();
p0.sendMessage(ChatColor.GREEN + "Reset all IP ratelimits on listener: " + ChatColor.WHITE + ll.getHostString());
}else {
for(ListenerInfo l : BungeeCord.getInstance().config.getListeners()) {
if(l.getRateLimitIP() != null) l.getRateLimitIP().resetLimiters();
}
p0.sendMessage(ChatColor.GREEN + "Reset all IP ratelimits.");
}
return;
}else if("login".equalsIgnoreCase(p1[1])) {
if(ll != null) {
if(ll.getRateLimitLogin() != null) ll.getRateLimitLogin().resetLimiters();
p0.sendMessage(ChatColor.GREEN + "Reset all login ratelimits on listener: " + ChatColor.WHITE + ll.getHostString());
}else {
for(ListenerInfo l : BungeeCord.getInstance().config.getListeners()) {
if(l.getRateLimitLogin() != null) l.getRateLimitLogin().resetLimiters();
}
p0.sendMessage(ChatColor.GREEN + "Reset all login ratelimits.");
}
return;
}else if("motd".equalsIgnoreCase(p1[1])) {
if(ll != null) {
if(ll.getRateLimitMOTD() != null) ll.getRateLimitMOTD().resetLimiters();
p0.sendMessage(ChatColor.GREEN + "Reset all MOTD ratelimits on listener: " + ChatColor.WHITE + ll.getHostString());
}else {
for(ListenerInfo l : BungeeCord.getInstance().config.getListeners()) {
if(l.getRateLimitMOTD() != null) l.getRateLimitMOTD().resetLimiters();
}
p0.sendMessage(ChatColor.GREEN + "Reset all MOTD ratelimits.");
}
return;
}else if("query".equalsIgnoreCase(p1[1])) {
if(ll != null) {
if(ll.getRateLimitMOTD() != null) ll.getRateLimitMOTD().resetLimiters();
p0.sendMessage(ChatColor.GREEN + "Reset all query ratelimits on listener: " + ChatColor.WHITE + ll.getHostString());
}else {
for(ListenerInfo l : BungeeCord.getInstance().config.getListeners()) {
if(l.getRateLimitMOTD() != null) l.getRateLimitMOTD().resetLimiters();
}
p0.sendMessage(ChatColor.GREEN + "Reset all query ratelimits.");
}
return;
}
}
}
p0.sendMessage(ChatColor.RED + "How to reset all rate limits: " + ChatColor.WHITE + "/eag-ratelimit reset");
p0.sendMessage(ChatColor.RED + "How to reset a specific rate limit: " + ChatColor.WHITE + "/eag-ratelimit reset <ip|login|motd|query>");
p0.sendMessage(ChatColor.RED + "How to reset a specific listener: " + ChatColor.WHITE + "/eag-ratelimit reset <all|ip|login|motd|query> <host>");
}
}

View File

@ -40,10 +40,17 @@ public class Configuration {
this.listeners = adapter.getListeners(); this.listeners = adapter.getListeners();
this.timeout = adapter.getInt("timeout", this.timeout); this.timeout = adapter.getInt("timeout", this.timeout);
this.uuid = adapter.getString("stats", this.uuid); this.uuid = adapter.getString("stats", this.uuid);
if(this.uuid.equalsIgnoreCase("595698b3-9c36-4e86-b1ee-cb3027038f41")) {
this.uuid = UUID.randomUUID().toString();
System.err.println("Notice: this server has the stats UUID \"595698b3-9c36-4e86-b1ee-cb3027038f41\" which is a known duplicate");
System.err.println("It has been updated to \"" + this.uuid + "\". This is not an error");
adapter.getMap().put("stats", this.uuid);
adapter.forceSave();
}
this.authInfo = adapter.getAuthSettings(); this.authInfo = adapter.getAuthSettings();
this.onlineMode = false; this.onlineMode = false;
this.playerLimit = adapter.getInt("player_limit", this.playerLimit); this.playerLimit = adapter.getInt("player_limit", this.playerLimit);
this.name = adapter.getString("server_name", EaglercraftBungee.brand + " Server"); this.name = adapter.getString("server_name", EaglercraftBungee.name + " Server");
Preconditions.checkArgument(this.listeners != null && !this.listeners.isEmpty(), (Object) "No listeners defined."); Preconditions.checkArgument(this.listeners != null && !this.listeners.isEmpty(), (Object) "No listeners defined.");
final Map<String, ServerInfo> newServers = adapter.getServers(); final Map<String, ServerInfo> newServers = adapter.getServers();
Preconditions.checkArgument(newServers != null && !newServers.isEmpty(), (Object) "No servers defined"); Preconditions.checkArgument(newServers != null && !newServers.isEmpty(), (Object) "No servers defined");

View File

@ -8,10 +8,13 @@ import net.md_5.bungee.tab.ServerUnique;
import net.md_5.bungee.tab.GlobalPing; import net.md_5.bungee.tab.GlobalPing;
import net.md_5.bungee.tab.Global; import net.md_5.bungee.tab.Global;
import net.md_5.bungee.api.tab.TabListHandler; import net.md_5.bungee.api.tab.TabListHandler;
import net.md_5.bungee.eaglercraft.WebSocketRateLimiter;
import net.md_5.bungee.api.config.TexturePackInfo; import net.md_5.bungee.api.config.TexturePackInfo;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import java.util.HashSet; import java.util.HashSet;
import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.config.MOTDCacheConfiguration;
import java.util.Collection; import java.util.Collection;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.Iterator; import java.util.Iterator;
@ -24,6 +27,7 @@ import java.io.FileWriter;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.io.InputStream; import java.io.InputStream;
import java.util.Collections; import java.util.Collections;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.io.IOException; import java.io.IOException;
@ -69,7 +73,7 @@ public class YamlConfig implements ConfigurationAdapter {
permissions.put("default", Arrays.asList("bungeecord.command.server", "bungeecord.command.list")); permissions.put("default", Arrays.asList("bungeecord.command.server", "bungeecord.command.list"));
permissions.put("admin", Arrays.asList("bungeecord.command.alert", "bungeecord.command.end", "bungeecord.command.ip", "bungeecord.command.reload", permissions.put("admin", Arrays.asList("bungeecord.command.alert", "bungeecord.command.end", "bungeecord.command.ip", "bungeecord.command.reload",
"bungeecord.command.eag.ban", "bungeecord.command.eag.banwildcard", "bungeecord.command.eag.banip", "bungeecord.command.eag.banregex", "bungeecord.command.eag.ban", "bungeecord.command.eag.banwildcard", "bungeecord.command.eag.banip", "bungeecord.command.eag.banregex",
"bungeecord.command.eag.reloadban", "bungeecord.command.eag.banned", "bungeecord.command.eag.banlist", "bungeecord.command.eag.unban")); "bungeecord.command.eag.reloadban", "bungeecord.command.eag.banned", "bungeecord.command.eag.banlist", "bungeecord.command.eag.unban", "bungeecord.command.eag.ratelimit"));
} }
this.get("groups", new HashMap<String, Object>()); this.get("groups", new HashMap<String, Object>());
} }
@ -173,15 +177,76 @@ public class YamlConfig implements ConfigurationAdapter {
final TexturePackInfo texture = (textureURL == null) ? null : new TexturePackInfo(textureURL, textureSize); final TexturePackInfo texture = (textureURL == null) ? null : new TexturePackInfo(textureURL, textureSize);
final String tabListName = this.get("tab_list", "GLOBAL_PING", val); final String tabListName = this.get("tab_list", "GLOBAL_PING", val);
final String serverIcon = this.get("server_icon", "server-icon.png", val); final String serverIcon = this.get("server_icon", "server-icon.png", val);
final boolean allowMOTD = this.get("allow_motd", true, val);
final boolean allowQuery = this.get("allow_query", true, val);
final MOTDCacheConfiguration cacheConfig = readCacheConfiguration(this.get("request_motd_cache", new HashMap<String, Object>(), val));
WebSocketRateLimiter ratelimitIP = null;
WebSocketRateLimiter ratelimitLogin = null;
WebSocketRateLimiter ratelimitMOTD = null;
WebSocketRateLimiter ratelimitQuery = null;
final Map<String, Object> rateLimits = this.get("ratelimit", new HashMap<String, Object>(), val);
final Map<String, Object> ratelimitIPConfig = this.get("ip", new HashMap<String, Object>(), rateLimits);
final Map<String, Object> ratelimitLoginConfig = this.get("login", new HashMap<String, Object>(), rateLimits);
final Map<String, Object> ratelimitMOTDConfig = this.get("motd", new HashMap<String, Object>(), rateLimits);
final Map<String, Object> ratelimitQueryConfig = this.get("query", new HashMap<String, Object>(), rateLimits);
if(this.get("enable", true, ratelimitIPConfig)) {
ratelimitIP = new WebSocketRateLimiter(
this.get("period", 90, ratelimitIPConfig),
this.get("limit", 60, ratelimitIPConfig),
this.get("limit_lockout", 80, ratelimitIPConfig),
this.get("lockout_duration", 1200, ratelimitIPConfig),
this.get("exceptions", new ArrayList<String>(), ratelimitIPConfig)
);
}
if(this.get("enable", true, ratelimitLoginConfig)) {
ratelimitLogin = new WebSocketRateLimiter(
this.get("period", 50, ratelimitLoginConfig),
this.get("limit", 5, ratelimitLoginConfig),
this.get("limit_lockout", 10, ratelimitLoginConfig),
this.get("lockout_duration", 300, ratelimitLoginConfig),
this.get("exceptions", new ArrayList<String>(), ratelimitLoginConfig)
);
}
if(this.get("enable", true, ratelimitMOTDConfig)) {
ratelimitMOTD = new WebSocketRateLimiter(
this.get("period", 30, ratelimitMOTDConfig),
this.get("limit", 5, ratelimitMOTDConfig),
this.get("limit_lockout", 15, ratelimitMOTDConfig),
this.get("lockout_duration", 1200, ratelimitMOTDConfig),
this.get("exceptions", new ArrayList<String>(), ratelimitMOTDConfig)
);
}
if(this.get("enable", true, ratelimitQueryConfig)) {
ratelimitQuery = new WebSocketRateLimiter(
this.get("period", 90, ratelimitQueryConfig),
this.get("limit", 60, ratelimitQueryConfig),
this.get("limit_lockout", 80, ratelimitQueryConfig),
this.get("lockout_duration", 1200, ratelimitQueryConfig),
this.get("exceptions", new ArrayList<String>(), ratelimitQueryConfig)
);
}
DefaultTabList value = DefaultTabList.valueOf(tabListName.toUpperCase()); DefaultTabList value = DefaultTabList.valueOf(tabListName.toUpperCase());
if (value == null) { if (value == null) {
value = DefaultTabList.GLOBAL_PING; value = DefaultTabList.GLOBAL_PING;
} }
final ListenerInfo info = new ListenerInfo(address, motd, maxPlayers, tabListSize, defaultServer, fallbackServer, forceDefault, websocket, forwardIp, forced, texture, value.clazz, serverIcon); ret.add(new ListenerInfo(host, address, motd, maxPlayers, tabListSize, defaultServer, fallbackServer, forceDefault, websocket, forwardIp,
ret.add(info); forced, texture, value.clazz, serverIcon, cacheConfig, allowMOTD, allowQuery, ratelimitIP, ratelimitLogin, ratelimitMOTD, ratelimitQuery));
} }
return ret; return ret;
} }
private MOTDCacheConfiguration readCacheConfiguration(Map<String, Object> val) {
final int ttl = this.get("cache_ttl", 7200, val);
final boolean anim = this.get("online_server_list_animation", false, val);
final boolean results = this.get("online_server_list_results", true, val);
final boolean trending = this.get("online_server_list_trending", true, val);
final boolean portfolios = this.get("online_server_list_portfolios", false, val);
return new MOTDCacheConfiguration(ttl, anim, results, trending, portfolios);
}
@Override @Override
public Collection<String> getGroups(final String player) { public Collection<String> getGroups(final String player) {
@ -212,4 +277,15 @@ public class YamlConfig implements ConfigurationAdapter {
//return new AuthServiceInfo(this.get("enabled", true, auth), this.get("limbo", "lobby", auth), new File(this.get("authfile", "passwords.yml", auth)), this.get("timeout", 30, auth)); //return new AuthServiceInfo(this.get("enabled", true, auth), this.get("limbo", "lobby", auth), new File(this.get("authfile", "passwords.yml", auth)), this.get("timeout", 30, auth));
return null; return null;
} }
@Override
public Map<String, Object> getMap() {
return config;
}
@Override
public void forceSave() {
this.save();
}
} }

View File

@ -234,6 +234,15 @@ public class BanList {
} }
} }
public static boolean isBlockedBan(InetAddress addr) {
for(IPBan b : BanList.blockedBans) {
if(b.checkBan(addr)) {
return true;
}
}
return false;
}
private static long lastListTest = 0l; private static long lastListTest = 0l;
private static long lastListLoad = 0l; private static long lastListLoad = 0l;
private static boolean fileIsBroken = false; private static boolean fileIsBroken = false;
@ -684,7 +693,7 @@ public class BanList {
p.println("[Wildcards]"); p.println("[Wildcards]");
p.println(); p.println();
p.println("# *fuck*"); p.println("# *fuck*");
p.println("# nigg*"); p.println("# shi*");
p.println(); p.println();
p.println(); p.println();
p.println("[Regex]"); p.println("[Regex]");

View File

@ -1,9 +1,10 @@
package net.md_5.bungee.eaglercraft; package net.md_5.bungee.eaglercraft;
public class EaglercraftBungee { public class EaglercraftBungee {
public static final String brand = "Eagtek"; public static final String brand = "Eagtek";
public static final String version = "0.1.0"; public static final String name = "Eaglercraftbungee";
public static final String version = "0.2.0";
public static final boolean cracked = true; public static final boolean cracked = true;
} }

View File

@ -13,6 +13,7 @@ import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.MOTD; import net.md_5.bungee.api.MOTD;
import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.config.MOTDCacheConfiguration;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
public class MOTDConnectionImpl extends QueryConnectionImpl implements MOTD { public class MOTDConnectionImpl extends QueryConnectionImpl implements MOTD {
@ -25,8 +26,9 @@ public class MOTDConnectionImpl extends QueryConnectionImpl implements MOTD {
private int maxPlayers; private int maxPlayers;
private boolean hasIcon; private boolean hasIcon;
private boolean iconDirty; private boolean iconDirty;
private String subType;
public MOTDConnectionImpl(ListenerInfo listener, InetAddress addr, WebSocket socket) { public MOTDConnectionImpl(ListenerInfo listener, InetAddress addr, WebSocket socket, String arg1) {
super(listener, addr, socket, "motd"); super(listener, addr, socket, "motd");
String[] lns = listener.getMotd().split("\n"); String[] lns = listener.getMotd().split("\n");
if(lns.length >= 1) { if(lns.length >= 1) {
@ -46,11 +48,22 @@ public class MOTDConnectionImpl extends QueryConnectionImpl implements MOTD {
} }
} }
bitmap = new int[4096]; bitmap = new int[4096];
iconDirty = hasIcon = listener.isIconSet();
if(hasIcon) {
System.arraycopy(listener.getServerIconCache(), 0, bitmap, 0, 4096);
}
setReturnType("motd"); setReturnType("motd");
int i = arg1.indexOf('.');
if(i > 0) {
subType = arg1.substring(i + 1);
if(subType.length() == 0) {
subType = "motd";
}
}else {
subType = "motd";
}
if(!subType.startsWith("noicon") && !subType.startsWith("cache.noicon")) {
iconDirty = hasIcon = listener.isIconSet();
if(hasIcon) {
System.arraycopy(listener.getServerIconCache(), 0, bitmap, 0, 4096);
}
}
} }
@Override @Override
@ -82,6 +95,11 @@ public class MOTDConnectionImpl extends QueryConnectionImpl implements MOTD {
public int getMaxPlayers() { public int getMaxPlayers() {
return maxPlayers; return maxPlayers;
} }
@Override
public String getSubType() {
return subType;
}
@Override @Override
public void setLine1(String p) { public void setLine1(String p) {
@ -123,11 +141,39 @@ public class MOTDConnectionImpl extends QueryConnectionImpl implements MOTD {
public void sendToUser() { public void sendToUser() {
if(!isClosed()) { if(!isClosed()) {
JSONObject obj = new JSONObject(); JSONObject obj = new JSONObject();
if(subType.startsWith("cache.anim")) {
obj.put("unsupported", true);
writeResponse(obj);
close();
return;
}else if(subType.startsWith("cache")) {
JSONArray cacheControl = new JSONArray();
MOTDCacheConfiguration cc = listener.getCacheConfig();
if(cc.cacheServerListAnimation) {
cacheControl.put("animation");
}
if(cc.cacheServerListResults) {
cacheControl.put("results");
}
if(cc.cacheServerListTrending) {
cacheControl.put("trending");
}
if(cc.cacheServerListPortfolios) {
cacheControl.put("portfolio");
}
obj.put("cache", cacheControl);
obj.put("ttl", cc.cacheTTL);
}else {
MOTDCacheConfiguration cc = listener.getCacheConfig();
obj.put("cache", cc.cacheServerListAnimation || cc.cacheServerListResults ||
cc.cacheServerListTrending || cc.cacheServerListPortfolios);
}
boolean noIcon = subType.startsWith("noicon") || subType.startsWith("cache.noicon");
JSONArray motd = new JSONArray(); JSONArray motd = new JSONArray();
if(line1 != null && line1.length() > 0) motd.put(line1); if(line1 != null && line1.length() > 0) motd.put(line1);
if(line2 != null && line2.length() > 0) motd.put(line2); if(line2 != null && line2.length() > 0) motd.put(line2);
obj.put("motd", motd); obj.put("motd", motd);
obj.put("icon", hasIcon); obj.put("icon", hasIcon && !noIcon);
obj.put("online", onlinePlayers); obj.put("online", onlinePlayers);
obj.put("max", maxPlayers); obj.put("max", maxPlayers);
JSONArray playerz = new JSONArray(); JSONArray playerz = new JSONArray();
@ -136,7 +182,7 @@ public class MOTDConnectionImpl extends QueryConnectionImpl implements MOTD {
} }
obj.put("players", playerz); obj.put("players", playerz);
writeResponse(obj); writeResponse(obj);
if(hasIcon && iconDirty && bitmap != null) { if(hasIcon && !noIcon && iconDirty && bitmap != null) {
byte[] iconPixels = new byte[16384]; byte[] iconPixels = new byte[16384];
for(int i = 0; i < 4096; ++i) { for(int i = 0; i < 4096; ++i) {
iconPixels[i * 4] = (byte)((bitmap[i] >> 16) & 0xFF); iconPixels[i * 4] = (byte)((bitmap[i] >> 16) & 0xFF);
@ -147,6 +193,9 @@ public class MOTDConnectionImpl extends QueryConnectionImpl implements MOTD {
writeResponseBinary(iconPixels); writeResponseBinary(iconPixels);
iconDirty = false; iconDirty = false;
} }
if(subType.startsWith("cache")) {
close();
}
} }
} }

View File

@ -1,5 +1,7 @@
package net.md_5.bungee.eaglercraft; package net.md_5.bungee.eaglercraft;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
@ -16,21 +18,34 @@ import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.event.WebsocketMOTDEvent; import net.md_5.bungee.api.event.WebsocketMOTDEvent;
import net.md_5.bungee.api.event.WebsocketQueryEvent; import net.md_5.bungee.api.event.WebsocketQueryEvent;
import net.md_5.bungee.eaglercraft.WebSocketRateLimiter.RateLimit;
public class WebSocketListener extends WebSocketServer { public class WebSocketListener extends WebSocketServer {
public static final String queryResponseBlocked = "{\"type\":\"blocked\"}";
public static final String queryResponseLockout = "{\"type\":\"locked\"}";
public static final String ipBlockedString = "BLOCKED";
public static final String ipLockedString = "LOCKED";
public static class PendingSocket { public static class PendingSocket {
public long openTime; public long openTime;
public InetAddress realAddress; public InetAddress realAddress;
protected PendingSocket(long openTime, InetAddress realAddress) { public boolean bypassBan;
protected PendingSocket(long openTime, InetAddress realAddress, boolean bypassBan) {
this.openTime = openTime; this.openTime = openTime;
this.realAddress = realAddress; this.realAddress = realAddress;
this.bypassBan = bypassBan;
} }
} }
private InetSocketAddress bungeeProxy; private InetSocketAddress bungeeProxy;
private ProxyServer bungeeCord; private ProxyServer bungeeCord;
private ListenerInfo info; private ListenerInfo info;
private final WebSocketRateLimiter ratelimitIP;
private final WebSocketRateLimiter ratelimitLogin;
private final WebSocketRateLimiter ratelimitMOTD;
private final WebSocketRateLimiter ratelimitQuery;
public WebSocketListener(ListenerInfo info, InetSocketAddress sock, ProxyServer bungeeCord) { public WebSocketListener(ListenerInfo info, InetSocketAddress sock, ProxyServer bungeeCord) {
super(info.getHost()); super(info.getHost());
@ -40,6 +55,22 @@ public class WebSocketListener extends WebSocketServer {
this.info = info; this.info = info;
this.bungeeProxy = sock; this.bungeeProxy = sock;
this.bungeeCord = bungeeCord; this.bungeeCord = bungeeCord;
this.ratelimitIP = info.getRateLimitIP();
this.ratelimitLogin = info.getRateLimitLogin();
this.ratelimitMOTD = info.getRateLimitMOTD();
this.ratelimitQuery = info.getRateLimitQuery();
if(this.ratelimitIP != null) {
this.ratelimitIP.resetLimiters();
}
if(this.ratelimitLogin != null) {
this.ratelimitLogin.resetLimiters();
}
if(this.ratelimitMOTD != null) {
this.ratelimitMOTD.resetLimiters();
}
if(this.ratelimitQuery != null) {
this.ratelimitQuery.resetLimiters();
}
} }
@Override @Override
@ -50,7 +81,6 @@ public class WebSocketListener extends WebSocketServer {
((WebSocketProxy)arg0.getAttachment()).killConnection(); ((WebSocketProxy)arg0.getAttachment()).killConnection();
} }
} }
System.out.println("websocket closed - " + arg0.getRemoteSocketAddress());
} }
@Override @Override
@ -69,14 +99,48 @@ public class WebSocketListener extends WebSocketServer {
arg1 = arg1.substring(7).trim(); arg1 = arg1.substring(7).trim();
QueryConnectionImpl con; QueryConnectionImpl con;
WebsocketQueryEvent evt; WebsocketQueryEvent evt;
if(arg1.equals("motd")) { if(arg1.startsWith("motd")) {
System.out.println("requested motd - " + realAddr); if(info.isAllowMOTD()) {
con = new MOTDConnectionImpl(info, realAddr, arg0); if(ratelimitMOTD != null && !BanList.isBlockedBan(realAddr)) {
evt = new WebsocketMOTDEvent((MOTD)con); RateLimit l = ratelimitMOTD.rateLimit(realAddr);
if(l.blocked()) {
if(l == RateLimit.LIMIT) {
arg0.send(queryResponseBlocked);
}else if(l == RateLimit.NOW_LOCKED_OUT) {
arg0.send(queryResponseLockout);
}
arg0.close();
return;
}
}
con = new MOTDConnectionImpl(info, realAddr, arg0, arg1);
evt = new WebsocketMOTDEvent((MOTD)con);
}else {
arg0.send(queryResponseBlocked);
arg0.close();
return;
}
}else { }else {
System.out.println("connection is query - accepts '" + arg1 + "' - " + realAddr); if(info.isAllowQuery()) {
con = new QueryConnectionImpl(info, realAddr, arg0, arg1); if(ratelimitQuery != null && !BanList.isBlockedBan(realAddr)) {
evt = new WebsocketQueryEvent(con); RateLimit l = ratelimitQuery.rateLimit(realAddr);
if(l.blocked()) {
if(l == RateLimit.LIMIT) {
arg0.send(queryResponseBlocked);
}else if(l == RateLimit.NOW_LOCKED_OUT) {
arg0.send(queryResponseLockout);
}
arg0.close();
return;
}
}
con = new QueryConnectionImpl(info, realAddr, arg0, arg1);
evt = new WebsocketQueryEvent(con);
}else {
arg0.send(queryResponseBlocked);
arg0.close();
return;
}
} }
BungeeCord.getInstance().getPluginManager().callEvent(evt); BungeeCord.getInstance().getPluginManager().callEvent(evt);
if(!con.isClosed() && (con instanceof MOTDConnectionImpl)) { if(!con.isClosed() && (con instanceof MOTDConnectionImpl)) {
@ -90,14 +154,11 @@ public class WebSocketListener extends WebSocketServer {
} }
} }
}else { }else {
System.err.println("unknown accept type - " + arg0.getRemoteSocketAddress());
arg0.close(); arg0.close();
} }
return; return;
}else if(o instanceof QueryConnectionImpl) { }else if(o instanceof QueryConnectionImpl) {
((QueryConnectionImpl)o).postMessage(arg1); ((QueryConnectionImpl)o).postMessage(arg1);
}else {
System.out.println("error: recieved text data on binary websocket - " + arg0.getRemoteSocketAddress());
} }
}else { }else {
arg0.close(); arg0.close();
@ -114,7 +175,18 @@ public class WebSocketListener extends WebSocketServer {
}else { }else {
realAddr = ((PendingSocket)o).realAddress; realAddr = ((PendingSocket)o).realAddress;
} }
System.out.println("connection is binary - " + realAddr); if(ratelimitLogin != null && !BanList.isBlockedBan(realAddr)) {
RateLimit l = ratelimitLogin.rateLimit(realAddr);
if(l.blocked()) {
if(l == RateLimit.LIMIT) {
arg0.send(createRawKickPacket("BLOCKED"));
}else if(l == RateLimit.NOW_LOCKED_OUT) {
arg0.send(createRawKickPacket("LOCKED"));
}
arg0.close();
return;
}
}
WebSocketProxy proxyObj = new WebSocketProxy(arg0, realAddr, bungeeProxy); WebSocketProxy proxyObj = new WebSocketProxy(arg0, realAddr, bungeeProxy);
arg0.setAttachment(proxyObj); arg0.setAttachment(proxyObj);
if(!proxyObj.connect()) { if(!proxyObj.connect()) {
@ -128,7 +200,6 @@ public class WebSocketListener extends WebSocketServer {
if(o instanceof WebSocketProxy) { if(o instanceof WebSocketProxy) {
((WebSocketProxy)o).sendPacket(arg1); ((WebSocketProxy)o).sendPacket(arg1);
}else { }else {
System.out.println("error: recieved binary data on text websocket - " + arg0.getRemoteSocketAddress());
arg0.close(); arg0.close();
} }
} }
@ -136,24 +207,37 @@ public class WebSocketListener extends WebSocketServer {
@Override @Override
public void onOpen(WebSocket arg0, ClientHandshake arg1) { public void onOpen(WebSocket arg0, ClientHandshake arg1) {
System.out.println("websocket opened - " + arg0.getRemoteSocketAddress()); InetAddress addr;
if(info.hasForwardedHeaders()) { if(info.hasForwardedHeaders()) {
String s = arg1.getFieldValue("X-Real-IP"); String s = arg1.getFieldValue("X-Real-IP");
if(s != null) { if(s != null) {
try { try {
InetAddress addr = InetAddress.getByName(s); addr = InetAddress.getByName(s);
arg0.setAttachment(new PendingSocket(System.currentTimeMillis(), addr));
System.out.println("real IP of '" + arg0.getRemoteSocketAddress().toString() + "' is '" + addr.getHostAddress() + "'");
}catch(UnknownHostException e) { }catch(UnknownHostException e) {
System.out.println("invalid 'X-Real-IP' header - " + e.toString()); System.out.println("invalid 'X-Real-IP' header - " + e.toString());
arg0.close(); arg0.close();
return;
} }
}else { }else {
arg0.setAttachment(new PendingSocket(System.currentTimeMillis(), arg0.getRemoteSocketAddress().getAddress())); addr = arg0.getRemoteSocketAddress().getAddress();
} }
}else { }else {
arg0.setAttachment(new PendingSocket(System.currentTimeMillis(), arg0.getRemoteSocketAddress().getAddress())); addr = arg0.getRemoteSocketAddress().getAddress();
} }
boolean bypassBan = BanList.isBlockedBan(addr);
if(!bypassBan && ratelimitIP != null) {
RateLimit l = ratelimitIP.rateLimit(addr);
if(l.blocked()) {
if(l == RateLimit.LIMIT) {
arg0.send(ipBlockedString);
}else if(l == RateLimit.NOW_LOCKED_OUT) {
arg0.send(ipLockedString);
}
arg0.close();
return;
}
}
arg0.setAttachment(new PendingSocket(System.currentTimeMillis(), addr, bypassBan));
} }
@Override @Override
@ -164,11 +248,9 @@ public class WebSocketListener extends WebSocketServer {
for(WebSocket w : this.getConnections()) { for(WebSocket w : this.getConnections()) {
Object o = w.getAttachment(); Object o = w.getAttachment();
if(o == null) { if(o == null) {
System.out.println("close inactive websocket - " + w.getRemoteSocketAddress());
w.close(); w.close();
}else if(o instanceof PendingSocket) { }else if(o instanceof PendingSocket) {
if(System.currentTimeMillis() - ((PendingSocket)o).openTime > 5000l) { if(System.currentTimeMillis() - ((PendingSocket)o).openTime > 1500l) {
System.out.println("close inactive websocket - " + ((PendingSocket)o).realAddress);
w.close(); w.close();
} }
} }
@ -185,5 +267,22 @@ public class WebSocketListener extends WebSocketServer {
} }
super.stop(); super.stop();
} }
private byte[] createRawKickPacket(String str) {
ByteArrayOutputStream bao = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bao);
try {
dout.write(255);
dout.writeShort(str.length());
dout.writeChars(str);
return bao.toByteArray();
}catch(IOException e) {
return new byte[] { (byte)255, 0, 0 };
}
}
public ListenerInfo getInfo() {
return info;
}
} }

View File

@ -0,0 +1,171 @@
package net.md_5.bungee.eaglercraft;
import java.net.InetAddress;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class WebSocketRateLimiter {
public static enum RateLimit {
NONE, LIMIT, LOCKED_OUT, NOW_LOCKED_OUT;
public boolean blocked() {
return this != NONE;
}
}
public final int period;
public final int limit;
public final int lockoutLimit;
public final int lockoutTime;
public final Collection<String> exceptions;
protected final Map<String, RateLimiter> ratelimiters = new HashMap();
public WebSocketRateLimiter(int period, int limit, int lockoutLimit, int lockoutTime, Collection<String> exceptions) {
this.period = period;
this.limit = limit;
this.lockoutLimit = lockoutLimit;
this.lockoutTime = lockoutTime;
this.exceptions = exceptions;
}
protected static class RateLimiter {
protected final WebSocketRateLimiter limiterConfig;
protected RateLimiter(WebSocketRateLimiter limiterConfig) {
this.limiterConfig = limiterConfig;
this.cooldownTimestamp = System.currentTimeMillis();
}
protected int requestCounter = 0;
protected long lockoutTimestamp = 0l;
protected long cooldownTimestamp;
private boolean checkLockout(long currentTimeMillis) {
if(lockoutTimestamp > 0l) {
if(currentTimeMillis - lockoutTimestamp < (long)(limiterConfig.lockoutTime * 1000l)) {
return true;
}else {
lockoutTimestamp = 0l;
requestCounter = 0;
cooldownTimestamp = currentTimeMillis;
}
}
return false;
}
private boolean checkCooldown(long currentTimeMillis) {
long cooldownIncrement = limiterConfig.period * 1000 / limiterConfig.limit;
while(currentTimeMillis - cooldownTimestamp > cooldownIncrement && requestCounter > 0) {
--requestCounter;
cooldownTimestamp += cooldownIncrement;
}
if(requestCounter == 0) {
cooldownTimestamp = currentTimeMillis;
return false;
}else {
return requestCounter >= limiterConfig.limit;
}
}
protected RateLimit increment() {
long t = System.currentTimeMillis();
if(checkLockout(t)) {
return RateLimit.LOCKED_OUT;
}
++requestCounter;
boolean blockByCooldown = checkCooldown(t);
if(requestCounter >= limiterConfig.lockoutLimit) {
requestCounter = 0;
cooldownTimestamp = t;
lockoutTimestamp = t;
return RateLimit.NOW_LOCKED_OUT;
}
if(blockByCooldown) {
return RateLimit.LIMIT;
}else {
return RateLimit.NONE;
}
}
protected RateLimit checkLimited() {
long t = System.currentTimeMillis();
if(checkLockout(t)) {
return RateLimit.LOCKED_OUT;
}else if(checkCooldown(t)) {
return RateLimit.LIMIT;
}else {
return RateLimit.NONE;
}
}
protected boolean checkClear() {
long t = System.currentTimeMillis();
if(checkLockout(t) || checkCooldown(t)) {
return false;
}else if(requestCounter > 0) {
return false;
}else {
return true;
}
}
}
public void resetLimiters() {
synchronized(ratelimiters) {
ratelimiters.clear();
}
}
public void deleteClearLimiters() {
synchronized(ratelimiters) {
Iterator<RateLimiter> itr = ratelimiters.values().iterator();
while(itr.hasNext()) {
if(itr.next().checkClear()) {
itr.remove();
}
}
}
}
public RateLimit checkLimit(InetAddress identifier) {
return checkLimit(identifier.getHostAddress());
}
public RateLimit rateLimit(InetAddress identifier) {
return rateLimit(identifier.getHostAddress());
}
public RateLimit checkLimit(String identifier) {
if(exceptions.contains(identifier)) {
return RateLimit.NONE;
}
synchronized(ratelimiters) {
RateLimiter l = ratelimiters.get(identifier);
if(l == null) {
return RateLimit.NONE;
}else {
return l.checkLimited();
}
}
}
public RateLimit rateLimit(String identifier) {
if(exceptions.contains(identifier)) {
return RateLimit.NONE;
}
synchronized(ratelimiters) {
RateLimiter l = ratelimiters.get(identifier);
if(l == null) {
l = new RateLimiter(this);
ratelimiters.put(identifier, l);
}
return l.increment();
}
}
}

View File

@ -205,8 +205,6 @@ public class IPAddressUtil {
public static boolean match(char paramChar, long paramLong1, long paramLong2) { public static boolean match(char paramChar, long paramLong1, long paramLong2) {
if (paramChar < '@') if (paramChar < '@')
return ((1L << paramChar & paramLong1) != 0L); return ((1L << paramChar & paramLong1) != 0L);
if (paramChar < '€')
return ((1L << paramChar - 64 & paramLong2) != 0L);
return false; return false;
} }

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -202,10 +202,40 @@ disconnect.closed=Connection closed
disconnect.loginFailed=Failed to login disconnect.loginFailed=Failed to login
disconnect.loginFailedInfo=Failed to login: %s disconnect.loginFailedInfo=Failed to login: %s
disconnect.quitting=Quitting disconnect.quitting=Quitting
disconnect.endOfStream=End of stream disconnect.endOfStream=End of stream (RIP)
disconnect.overflow=Buffer overflow disconnect.overflow=Buffer overflow
disconnect.spam=Kicked for spamming disconnect.spam=Kicked for spamming
disconnect.ratelimit.ipNowLocked.title=Connection Closed
disconnect.ratelimit.ipNowLocked.description0=Suspected Denial of Service
disconnect.ratelimit.ipNowLocked.description1=Your IP is now blocked
disconnect.ratelimit.ipNowLocked.tryAgain=please try again in an hour
disconnect.ratelimit.ipLocked.title=Connection Refused
disconnect.ratelimit.ipLocked.description0=Suspected Denial of Service
disconnect.ratelimit.ipLocked.description1=Your IP is still blocked
disconnect.ratelimit.ipLocked.tryAgain=please try again in an hour
disconnect.ratelimit.ipFailedPossiblyLocked.title=Connection Failed
disconnect.ratelimit.ipFailedPossiblyLocked.description0=You've been kicked by this server before for suspected
disconnect.ratelimit.ipFailedPossiblyLocked.description1=$Denial of Service (DoS), your IP is probably blocked
disconnect.ratelimit.ipFailedPossiblyLocked.tryAgain=please try again in an hour
disconnect.ratelimit.ipBlocked.title=Connection Closed
disconnect.ratelimit.ipBlocked.description0=Too many connections from your IP
disconnect.ratelimit.ipBlocked.description1=Please try again in a few minutes
disconnect.ratelimit.ipBlocked.tryAgain=(Suspected Denial of Service)
disconnect.ratelimit.kickBlocked.title=Connection Closed
disconnect.ratelimit.kickBlocked.description0=You're reconnecting too quickly!
disconnect.ratelimit.kickBlocked.description1=Please try again in a minute
disconnect.ratelimit.kickBlocked.tryAgain=(too many logins from your IP)
disconnect.ratelimit.kickLocked.title=Connection Closed
disconnect.ratelimit.kickLocked.description0=Suspected Denial of Service
disconnect.ratelimit.kickLocked.description1=Logins from you IP are now blocked
disconnect.ratelimit.kickLocked.tryAgain=please try again in an hour
options.off=OFF options.off=OFF
options.on=ON options.on=ON
options.visible=Shown options.visible=Shown

View File

@ -29,7 +29,9 @@ import java.nio.ByteOrder;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
@ -66,6 +68,7 @@ import net.lax1dude.eaglercraft.AssetRepository;
import net.lax1dude.eaglercraft.EarlyLoadScreen; import net.lax1dude.eaglercraft.EarlyLoadScreen;
import net.lax1dude.eaglercraft.ServerQuery; import net.lax1dude.eaglercraft.ServerQuery;
import net.lax1dude.eaglercraft.adapter.EaglerAdapterImpl2.ProgramGL; import net.lax1dude.eaglercraft.adapter.EaglerAdapterImpl2.ProgramGL;
import net.lax1dude.eaglercraft.adapter.EaglerAdapterImpl2.RateLimit;
import net.lax1dude.eaglercraft.adapter.lwjgl.GameWindowListener; import net.lax1dude.eaglercraft.adapter.lwjgl.GameWindowListener;
import net.minecraft.src.MathHelper; import net.minecraft.src.MathHelper;
import paulscode.sound.SoundSystem; import paulscode.sound.SoundSystem;
@ -853,6 +856,9 @@ public class EaglerAdapterImpl2 {
Display.sync(performanceToFps); Display.sync(performanceToFps);
} }
private static final Set<String> rateLimitedAddresses = new HashSet();
private static final Set<String> blockedAddresses = new HashSet();
private static WebSocketClient clientSocket = null; private static WebSocketClient clientSocket = null;
private static final Object socketSync = new Object(); private static final Object socketSync = new Object();
@ -861,15 +867,31 @@ public class EaglerAdapterImpl2 {
private static class EaglerSocketClient extends WebSocketClient { private static class EaglerSocketClient extends WebSocketClient {
private Exception currentException = null; private Exception currentException = null;
private boolean wasAbleToConnect = false;
private String serverUriString;
private boolean socketIsAlive = false;
public EaglerSocketClient(URI serverUri) throws IOException, InterruptedException { public EaglerSocketClient(URI serverUri, String str) throws IOException, InterruptedException {
super(serverUri); super(serverUri);
this.setTcpNoDelay(true); this.setTcpNoDelay(true);
this.setConnectionLostTimeout(5); this.setConnectionLostTimeout(5);
System.out.println("[ws] connecting to "+serverUri.toString()); System.out.println("[ws] connecting to "+serverUri.toString());
rateLimitStatus = null;
if(!this.connectBlocking(5, TimeUnit.SECONDS)) { if(!this.connectBlocking(5, TimeUnit.SECONDS)) {
synchronized(socketSync) {
if(rateLimitStatus == null) {
if(blockedAddresses.contains(str)) {
rateLimitStatus = RateLimit.BLOCKED;
}else if(rateLimitedAddresses.contains(str)) {
rateLimitStatus = RateLimit.FAILED_POSSIBLY_LOCKED;
}else {
rateLimitStatus = RateLimit.FAILED;
}
}
}
throw new IOException("could not connect socket", currentException); throw new IOException("could not connect socket", currentException);
} }
serverUriString = str;
} }
@Override @Override
@ -878,6 +900,17 @@ public class EaglerAdapterImpl2 {
readPackets.clear(); readPackets.clear();
System.out.println("[ws] disconnecting - " + currentException); System.out.println("[ws] disconnecting - " + currentException);
currentException = null; currentException = null;
if(!wasAbleToConnect && rateLimitStatus == null) {
if(blockedAddresses.contains(serverUriString)) {
rateLimitStatus = RateLimit.LOCKED;
}else if(rateLimitedAddresses.contains(serverUriString)) {
rateLimitStatus = RateLimit.FAILED_POSSIBLY_LOCKED;
}else {
rateLimitStatus = RateLimit.FAILED;
}
}else if(!socketIsAlive) {
rateLimitStatus = RateLimit.LOCKED;
}
} }
} }
@ -888,12 +921,28 @@ public class EaglerAdapterImpl2 {
@Override @Override
public void onMessage(String arg0) { public void onMessage(String arg0) {
System.out.println("[ws] " + arg0); wasAbleToConnect = true;
synchronized(socketSync) {
if(arg0.equalsIgnoreCase("BLOCKED")) {
rateLimitedAddresses.add(serverUriString);
if(rateLimitStatus == null) {
rateLimitStatus = RateLimit.BLOCKED;
}
}else if(arg0.equalsIgnoreCase("LOCKED")) {
blockedAddresses.add(serverUriString);
rateLimitedAddresses.add(serverUriString);
if(rateLimitStatus == null) {
rateLimitStatus = RateLimit.NOW_LOCKED;
}
}
}
this.close();
currentException = null; currentException = null;
} }
@Override @Override
public void onMessage(ByteBuffer arg0) { public void onMessage(ByteBuffer arg0) {
wasAbleToConnect = true;
synchronized(socketSync) { synchronized(socketSync) {
readPackets.add(arg0.array()); readPackets.add(arg0.array());
} }
@ -911,8 +960,9 @@ public class EaglerAdapterImpl2 {
if(clientSocket != null) { if(clientSocket != null) {
clientSocket.close(); clientSocket.close();
} }
rateLimitStatus = null;
try { try {
clientSocket = new EaglerSocketClient(new URI(uri)); clientSocket = new EaglerSocketClient(new URI(uri), uri);
return true; return true;
}catch(InterruptedException e) { }catch(InterruptedException e) {
clientSocket = null; clientSocket = null;
@ -948,6 +998,35 @@ public class EaglerAdapterImpl2 {
} }
return null; return null;
} }
private static RateLimit rateLimitStatus = null;
public static enum RateLimit {
NONE, FAILED, BLOCKED, FAILED_POSSIBLY_LOCKED, LOCKED, NOW_LOCKED;
}
public static final RateLimit getRateLimitStatus() {
RateLimit l = rateLimitStatus;
rateLimitStatus = null;
return l;
}
public static final void logRateLimit(String addr, RateLimit l) {
synchronized(socketSync) {
if(l == RateLimit.LOCKED) {
blockedAddresses.add(addr);
}else {
rateLimitedAddresses.add(addr);
}
}
}
public static final RateLimit checkRateLimitHistory(String addr) {
synchronized(socketSync) {
if(blockedAddresses.contains(addr)) {
return RateLimit.LOCKED;
}else if(rateLimitedAddresses.contains(addr)) {
return RateLimit.BLOCKED;
}else {
return RateLimit.NONE;
}
}
}
public static final byte[] loadLocalStorage(String key) { public static final byte[] loadLocalStorage(String key) {
try { try {
File f = new File("_eagstorage."+key+".dat"); File f = new File("_eagstorage."+key+".dat");
@ -1226,11 +1305,15 @@ public class EaglerAdapterImpl2 {
private final LinkedList<byte[]> queryResponsesBytes = new LinkedList(); private final LinkedList<byte[]> queryResponsesBytes = new LinkedList();
private final String type; private final String type;
private boolean open; private boolean open;
private boolean alive;
private String serverUri;
private ServerQueryImpl(String type, URI serverUri) { private ServerQueryImpl(String type, URI serverUri, String serverUriString) throws IOException {
super(serverUri); super(serverUri);
this.serverUri = serverUriString;
this.type = type; this.type = type;
this.open = true; this.open = true;
this.alive = false;
this.setConnectionLostTimeout(5); this.setConnectionLostTimeout(5);
this.setTcpNoDelay(true); this.setTcpNoDelay(true);
this.connect(); this.connect();
@ -1267,6 +1350,15 @@ public class EaglerAdapterImpl2 {
@Override @Override
public void onClose(int arg0, String arg1, boolean arg2) { public void onClose(int arg0, String arg1, boolean arg2) {
open = false; open = false;
if(!alive) {
synchronized(socketSync) {
if(EaglerAdapterImpl2.blockedAddresses.contains(serverUri)) {
queryResponses.add(new QueryResponse(true));
}else if(EaglerAdapterImpl2.rateLimitedAddresses.contains(serverUri)) {
queryResponses.add(new QueryResponse(false));
}
}
}
} }
@Override @Override
@ -1278,17 +1370,46 @@ public class EaglerAdapterImpl2 {
@Override @Override
public void onMessage(String arg0) { public void onMessage(String arg0) {
this.alive = true;
synchronized(queryResponses) { synchronized(queryResponses) {
try { if(arg0.equalsIgnoreCase("BLOCKED")) {
queryResponses.add(new QueryResponse(new JSONObject(arg0))); synchronized(socketSync) {
}catch(Throwable t) { EaglerAdapterImpl2.rateLimitedAddresses.add(serverUri);
System.err.println("Query response parse error: " + t.toString()); queryResponses.add(new QueryResponse(false));
}
this.close();
return;
}else if(arg0.equalsIgnoreCase("LOCKED")) {
synchronized(socketSync) {
EaglerAdapterImpl2.blockedAddresses.add(serverUri);
queryResponses.add(new QueryResponse(true));
}
this.close();
return;
}else {
try {
QueryResponse q = new QueryResponse(new JSONObject(arg0));
if(q.rateLimitStatus != null) {
synchronized(socketSync) {
if(q.rateLimitStatus == RateLimit.BLOCKED) {
EaglerAdapterImpl2.rateLimitedAddresses.add(serverUri);
}else if(q.rateLimitStatus == RateLimit.LOCKED) {
EaglerAdapterImpl2.blockedAddresses.add(serverUri);
}
}
this.close();
}
queryResponses.add(q);
}catch(Throwable t) {
System.err.println("Query response parse error: " + t.toString());
}
} }
} }
} }
@Override @Override
public void onMessage(ByteBuffer arg0) { public void onMessage(ByteBuffer arg0) {
this.alive = true;
synchronized(queryResponsesBytes) { synchronized(queryResponsesBytes) {
byte[] pkt = new byte[arg0.limit()]; byte[] pkt = new byte[arg0.limit()];
arg0.get(pkt); arg0.get(pkt);
@ -1310,7 +1431,7 @@ public class EaglerAdapterImpl2 {
public static final ServerQuery openQuery(String type, String uri) { public static final ServerQuery openQuery(String type, String uri) {
try { try {
return new ServerQueryImpl(type, new URI(uri)); return new ServerQueryImpl(type, new URI(uri), uri);
}catch(Throwable t) { }catch(Throwable t) {
System.err.println("WebSocket query error: " + t.toString()); System.err.println("WebSocket query error: " + t.toString());
return null; return null;

View File

@ -4,7 +4,7 @@ public class ConfigConstants {
public static boolean profanity = false; public static boolean profanity = false;
public static final String version = "22w13i"; public static final String version = "22w14a";
public static final String mainMenuString = "eaglercraft " + version; public static final String mainMenuString = "eaglercraft " + version;
public static final String forkMe = "https://github.com/LAX1DUDE/eaglercraft"; public static final String forkMe = "https://github.com/LAX1DUDE/eaglercraft";

View File

@ -325,12 +325,18 @@ public class DefaultSkinRenderer {
} }
if(isNew) { if(isNew) {
if((id < 0 && EaglerProfile.skins.get(id2).slim) || (id >= 0 && isAlexSkin(id))) { if((id < 0 && EaglerProfile.skins.get(id2).slim) || (id >= 0 && isAlexSkin(id))) {
newSkinRendererSlim.blockTransparentSkin = true;
newSkinRendererSlim.render(null, 0.0f, 0.0f, (float)(System.currentTimeMillis() % 100000) / 50f, ((x - mx) * 0.06f), ((y - my) * -0.1f), 0.0625F); newSkinRendererSlim.render(null, 0.0f, 0.0f, (float)(System.currentTimeMillis() % 100000) / 50f, ((x - mx) * 0.06f), ((y - my) * -0.1f), 0.0625F);
newSkinRendererSlim.blockTransparentSkin = false;
}else { }else {
newSkinRenderer.blockTransparentSkin = true;
newSkinRenderer.render(null, 0.0f, 0.0f, (float)(System.currentTimeMillis() % 100000) / 50f, ((x - mx) * 0.06f), ((y - my) * -0.1f), 0.0625F); newSkinRenderer.render(null, 0.0f, 0.0f, (float)(System.currentTimeMillis() % 100000) / 50f, ((x - mx) * 0.06f), ((y - my) * -0.1f), 0.0625F);
newSkinRenderer.blockTransparentSkin = false;
} }
}else { }else {
oldSkinRenderer.blockTransparentSkin = true;
oldSkinRenderer.render(null, 0.0f, 0.0f, (float)(System.currentTimeMillis() % 100000) / 50f, ((x - mx) * 0.06f), ((y - my) * -0.1f), 0.0625F); oldSkinRenderer.render(null, 0.0f, 0.0f, (float)(System.currentTimeMillis() % 100000) / 50f, ((x - mx) * 0.06f), ((y - my) * -0.1f), 0.0625F);
oldSkinRenderer.blockTransparentSkin = false;
} }
}else if(isZombieModel(id)) { }else if(isZombieModel(id)) {
if(zombieRenderer == null) zombieRenderer = new ModelZombie(0.0F, true); if(zombieRenderer == null) zombieRenderer = new ModelZombie(0.0F, true);

View File

@ -2,6 +2,8 @@ package net.lax1dude.eaglercraft;
import org.json.JSONObject; import org.json.JSONObject;
import net.lax1dude.eaglercraft.adapter.EaglerAdapterImpl2.RateLimit;
public interface ServerQuery { public interface ServerQuery {
public static final long defaultTimeout = 10000l; public static final long defaultTimeout = 10000l;
@ -15,15 +17,43 @@ public interface ServerQuery {
public final long serverTime; public final long serverTime;
public final long clientTime; public final long clientTime;
public final boolean serverCracked; public final boolean serverCracked;
public final RateLimit rateLimitStatus;
public final boolean rateLimitIsTCP;
public QueryResponse(JSONObject obj) { public QueryResponse(JSONObject obj) {
this.responseType = obj.getString("type").toLowerCase(); this.responseType = obj.getString("type").toLowerCase();
this.responseData = obj.get("data"); if(this.responseType.equals("blocked") || this.responseType.equals("locked")) {
this.serverVersion = obj.getString("vers"); this.responseData = null;
this.serverBrand = obj.getString("brand"); this.serverVersion = "Unknown";
this.serverName = obj.getString("name"); this.serverBrand = "Unknown";
this.serverTime = obj.getLong("time"); this.serverName = "Unknown";
this.serverTime = 0l;
this.clientTime = System.currentTimeMillis();
this.serverCracked = false;
this.rateLimitStatus = this.responseType.equals("locked") ? RateLimit.LOCKED : RateLimit.BLOCKED;
this.rateLimitIsTCP = false;
}else {
this.responseData = obj.get("data");
this.serverVersion = obj.getString("vers");
this.serverBrand = obj.getString("brand");
this.serverName = obj.getString("name");
this.serverTime = obj.getLong("time");
this.clientTime = System.currentTimeMillis();
this.serverCracked = obj.optBoolean("cracked", false);
this.rateLimitStatus = null;
this.rateLimitIsTCP = false;
}
}
public QueryResponse(boolean lockedNotBlocked) {
this.responseType = lockedNotBlocked ? "locked" : "blocked";
this.responseData = null;
this.serverVersion = "Unknown";
this.serverBrand = "Unknown";
this.serverName = "Unknown";
this.serverTime = 0l;
this.clientTime = System.currentTimeMillis(); this.clientTime = System.currentTimeMillis();
this.serverCracked = obj.has("cracked") ? obj.getBoolean("cracked") : false; this.serverCracked = false;
this.rateLimitStatus = lockedNotBlocked ? RateLimit.LOCKED : RateLimit.BLOCKED;
this.rateLimitIsTCP = true;
} }
public boolean isResponseString() { public boolean isResponseString() {
return responseData instanceof String; return responseData instanceof String;

View File

@ -366,21 +366,21 @@ public class TextureTerrainMap implements IconRegister {
for(int x = 0; x < src.w; ++x) { for(int x = 0; x < src.w; ++x) {
int pixel = src.data[y * src.w + x]; int pixel = src.data[y * src.w + x];
if(y != src.h - 1) { //if(y != src.h - 1) {
ret.data[(src.h - 1 - y) * ret.w + (x)] = pixel; ret.data[y * ret.w + (x)] = pixel;
ret.data[(src.h - 1 - y) * ret.w + (x + src.w)] = pixel; ret.data[y * ret.w + (x + src.w)] = pixel;
ret.data[(src.h - 1 - y) * ret.w + (x + src.w*2)] = pixel; ret.data[y * ret.w + (x + src.w*2)] = pixel;
} //}
ret.data[(y + src.h) * ret.w + (x)] = pixel; ret.data[(y + src.h) * ret.w + (x)] = pixel;
ret.data[(y + src.h) * ret.w + (x + src.w)] = pixel; ret.data[(y + src.h) * ret.w + (x + src.w)] = pixel;
ret.data[(y + src.h) * ret.w + (x + src.w*2)] = pixel; ret.data[(y + src.h) * ret.w + (x + src.w*2)] = pixel;
if(y != 0) { //if(y != 0) {
ret.data[(src.h*3 - 1 - y) * ret.w + (x)] = pixel; ret.data[(y + src.h * 2) * ret.w + (x)] = pixel;
ret.data[(src.h*3 - 1 - y) * ret.w + (x + src.w)] = pixel; ret.data[(y + src.h * 2) * ret.w + (x + src.w)] = pixel;
ret.data[(src.h*3 - 1 - y) * ret.w + (x + src.w*2)] = pixel; ret.data[(y + src.h * 2) * ret.w + (x + src.w*2)] = pixel;
} //}
} }
} }
return ret; return ret;

View File

@ -1089,6 +1089,7 @@ public class Minecraft implements Runnable {
} }
} }
GuiMultiplayer.tickRefreshCooldown();
GuiScreenVoiceChannel.tickVoiceConnection(); GuiScreenVoiceChannel.tickVoiceConnection();
if (this.currentScreen == null || this.currentScreen.allowUserInput) { if (this.currentScreen == null || this.currentScreen.allowUserInput) {

View File

@ -3,6 +3,7 @@ package net.minecraft.src;
public class EntityItemFrame extends EntityHanging { public class EntityItemFrame extends EntityHanging {
/** Chance for this item frame's item to drop from the frame. */ /** Chance for this item frame's item to drop from the frame. */
private float itemDropChance = 1.0F; private float itemDropChance = 1.0F;
private boolean hasMapItem = false;
public EntityItemFrame() { public EntityItemFrame() {
super(); super();
@ -19,11 +20,11 @@ public class EntityItemFrame extends EntityHanging {
} }
public int func_82329_d() { public int func_82329_d() {
return 9; return hasMapItem ? 17 : 10;
} }
public int func_82330_g() { public int func_82330_g() {
return 9; return hasMapItem ? 17 : 10;
} }
/** /**
@ -52,7 +53,13 @@ public class EntityItemFrame extends EntityHanging {
} }
public ItemStack getDisplayedItem() { public ItemStack getDisplayedItem() {
return this.getDataWatcher().getWatchableObjectItemStack(2); ItemStack stack = this.getDataWatcher().getWatchableObjectItemStack(2);
boolean isStackMap = stack != null && stack.getItem() == Item.map;
if(isStackMap != hasMapItem) {
hasMapItem = isStackMap;
this.setDirection(this.hangingDirection);
}
return stack;
} }
public void setDisplayedItem(ItemStack par1ItemStack) { public void setDisplayedItem(ItemStack par1ItemStack) {

View File

@ -191,12 +191,12 @@ public class FontRenderer {
float var3 = (float) (par1 % 16 * 8); float var3 = (float) (par1 % 16 * 8);
float var4 = (float) (par1 / 16 * 8); float var4 = (float) (par1 / 16 * 8);
float var5 = par2 ? 1.0F : 0.0F; float var5 = par2 ? 1.0F : 0.0F;
float var6 = (float) this.charWidth[par1] - 0.01F; float var6 = (float) this.charWidth[par1] - 0.02F;
Tessellator t = Tessellator.instance; Tessellator t = Tessellator.instance;
t.addVertexWithUV(this.posX + var5, this.posY, 0.0F, var3 / 128.0F, var4 / 128.0F); t.addVertexWithUV(this.posX + 0.02F + var5, this.posY + 0.02F, 0.0F, (var3 + 0.02F) / 128.0F, (var4 + 0.02F) / 128.0F);
t.addVertexWithUV(this.posX - var5, this.posY + 7.99F, 0.0F, var3 / 128.0F, (var4 + 7.99F) / 128.0F); t.addVertexWithUV(this.posX + 0.02F - var5, this.posY + 7.98F, 0.0F, (var3 + 0.02F) / 128.0F, (var4 + 7.98F) / 128.0F);
t.addVertexWithUV(this.posX + var6 - var5, this.posY + 7.99F, 0.0F, (var3 + var6) / 128.0F, (var4 + 7.99F) / 128.0F); t.addVertexWithUV(this.posX + var6 - var5, this.posY + 7.98F, 0.0F, (var3 + var6) / 128.0F, (var4 + 7.98F) / 128.0F);
t.addVertexWithUV(this.posX + var6 + var5, this.posY, 0.0F, (var3 + var6) / 128.0F, var4 / 128.0F); t.addVertexWithUV(this.posX + var6 + var5, this.posY + 0.02F, 0.0F, (var3 + var6) / 128.0F, (var4 + 0.02F) / 128.0F);
return (float) this.charWidth[par1]; return (float) this.charWidth[par1];
} }
@ -227,13 +227,13 @@ public class FontRenderer {
float var7 = (float) (var5 + 1); float var7 = (float) (var5 + 1);
float var8 = (float) (par1 % 16 * 16) + var6; float var8 = (float) (par1 % 16 * 16) + var6;
float var9 = (float) ((par1 & 255) / 16 * 16); float var9 = (float) ((par1 & 255) / 16 * 16);
float var10 = var7 - var6 - 0.02F; float var10 = var7 - var6 - 0.04F;
float var11 = par2 ? 1.0F : 0.0F; float var11 = par2 ? 1.0F : 0.0F;
t.startDrawing(EaglerAdapter.GL_TRIANGLE_STRIP); t.startDrawing(EaglerAdapter.GL_TRIANGLE_STRIP);
t.addVertexWithUV(this.posX + var11, this.posY, 0.0F, var8 / 256.0F, var9 / 256.0F); t.addVertexWithUV(this.posX + 0.02F + var11, this.posY + 0.02F, 0.0F, (var8 + 0.02F) / 256.0F, (var9 + 0.02F) / 256.0F);
t.addVertexWithUV(this.posX - var11, this.posY + 7.99F, 0.0F, var8 / 256.0F, (var9 + 15.98F) / 256.0F); t.addVertexWithUV(this.posX + 0.02F - var11, this.posY + 7.98F, 0.0F, (var8 + 0.02F) / 256.0F, (var9 + 15.98F) / 256.0F);
t.addVertexWithUV(this.posX + var10 / 2.0F + var11, this.posY, 0.0F, (var8 + var10) / 256.0F, var9 / 256.0F); t.addVertexWithUV(this.posX + var10 / 2.0F + var11, this.posY + 0.02F, 0.0F, (var8 + var10) / 256.0F, (var9 + 0.02F) / 256.0F);
t.addVertexWithUV(this.posX + var10 / 2.0F - var11, this.posY + 7.99F, 0.0F, (var8 + var10) / 256.0F, (var9 + 15.98F) / 256.0F); t.addVertexWithUV(this.posX + var10 / 2.0F - var11, this.posY + 7.98F, 0.0F, (var8 + var10) / 256.0F, (var9 + 15.98F) / 256.0F);
t.draw(); t.draw();
this.fontTexture.bindTexture(); this.fontTexture.bindTexture();
t.startDrawingQuads(); t.startDrawingQuads();

View File

@ -2,7 +2,9 @@ package net.minecraft.src;
import java.io.IOException; import java.io.IOException;
import net.lax1dude.eaglercraft.EaglerAdapter;
import net.lax1dude.eaglercraft.EaglerProfile; import net.lax1dude.eaglercraft.EaglerProfile;
import net.lax1dude.eaglercraft.adapter.EaglerAdapterImpl2.RateLimit;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
public class GuiConnecting extends GuiScreen { public class GuiConnecting extends GuiScreen {
@ -76,16 +78,39 @@ public class GuiConnecting extends GuiScreen {
this.clientHandler.addToSendQueue(new Packet2ClientProtocol(69, EaglerProfile.username, uria, port)); this.clientHandler.addToSendQueue(new Packet2ClientProtocol(69, EaglerProfile.username, uria, port));
this.clientHandler.addToSendQueue(new Packet250CustomPayload("EAG|MySkin", EaglerProfile.getSkinPacket())); this.clientHandler.addToSendQueue(new Packet250CustomPayload("EAG|MySkin", EaglerProfile.getSkinPacket()));
} catch (IOException e) { } catch (IOException e) {
try {
this.clientHandler.disconnect();
}catch(Throwable t) {
}
e.printStackTrace(); e.printStackTrace();
this.mc.displayGuiScreen(new GuiDisconnected(this.field_98098_c, "connect.failed", "disconnect.genericReason", "could not connect to "+uri, e.toString())); showDisconnectScreen(e.toString());
} }
} }
if(this.clientHandler != null) { if(this.clientHandler != null) {
this.clientHandler.processReadPackets(); this.clientHandler.processReadPackets();
} }
if(timer > 5) {
if(!EaglerAdapter.connectionOpen() && this.mc.currentScreen == this) {
showDisconnectScreen("");
}
}
++timer; ++timer;
} }
private void showDisconnectScreen(String e) {
RateLimit l = EaglerAdapter.getRateLimitStatus();
if(l == RateLimit.NOW_LOCKED) {
this.mc.displayGuiScreen(new GuiDisconnected(this.field_98098_c, "disconnect.ipNowLocked", "disconnect.endOfStream", null));
}else if(l == RateLimit.LOCKED) {
this.mc.displayGuiScreen(new GuiDisconnected(this.field_98098_c, "disconnect.ipLocked", "disconnect.endOfStream", null));
}else if(l == RateLimit.BLOCKED) {
this.mc.displayGuiScreen(new GuiDisconnected(this.field_98098_c, "disconnect.ipBlocked", "disconnect.endOfStream", null));
}else if(l == RateLimit.FAILED_POSSIBLY_LOCKED) {
this.mc.displayGuiScreen(new GuiDisconnected(this.field_98098_c, "disconnect.ipFailedPossiblyLocked", "disconnect.endOfStream", null));
}else {
this.mc.displayGuiScreen(new GuiDisconnected(this.field_98098_c, "connect.failed", "disconnect.genericReason", "could not connect to "+uri, e));
}
}
/** /**
* Fired when a key is typed. This is the equivalent of * Fired when a key is typed. This is the equivalent of

View File

@ -12,6 +12,11 @@ public class GuiDisconnected extends GuiScreen {
/** The details about the error. */ /** The details about the error. */
private String errorDetail; private String errorDetail;
private String errorDetail2;
private String errorDetailTryAgain;
private boolean kickForDoS;
private Object[] field_74247_c; private Object[] field_74247_c;
private List field_74245_d; private List field_74245_d;
private final GuiScreen field_98095_n; private final GuiScreen field_98095_n;
@ -19,8 +24,19 @@ public class GuiDisconnected extends GuiScreen {
public GuiDisconnected(GuiScreen par1GuiScreen, String par2Str, String par3Str, Object... par4ArrayOfObj) { public GuiDisconnected(GuiScreen par1GuiScreen, String par2Str, String par3Str, Object... par4ArrayOfObj) {
StringTranslate var5 = StringTranslate.getInstance(); StringTranslate var5 = StringTranslate.getInstance();
this.field_98095_n = par1GuiScreen; this.field_98095_n = par1GuiScreen;
this.errorMessage = par2Str.equals("disconnect.requiresAuth") ? par2Str : var5.translateKey(par2Str); if(par2Str.startsWith("disconnect.ratelimit")) {
this.errorDetail = par3Str; this.errorMessage = var5.translateKey(par2Str + ".title");
this.errorDetail = var5.translateKey(par2Str + ".description0");
this.errorDetail2 = var5.translateKey(par2Str + ".description1");
this.errorDetailTryAgain = var5.translateKey(par2Str + ".tryAgain");
this.kickForDoS = true;
}else {
this.errorMessage = par2Str.equals("disconnect.requiresAuth") ? par2Str : var5.translateKey(par2Str);
this.errorDetail = par3Str;
this.errorDetail2 = null;
this.errorDetailTryAgain = null;
this.kickForDoS = false;
}
this.field_74247_c = par4ArrayOfObj; this.field_74247_c = par4ArrayOfObj;
} }
@ -37,15 +53,25 @@ public class GuiDisconnected extends GuiScreen {
public void initGui() { public void initGui() {
StringTranslate var1 = StringTranslate.getInstance(); StringTranslate var1 = StringTranslate.getInstance();
this.buttonList.clear(); this.buttonList.clear();
this.buttonList.add(new GuiButton(0, this.width / 2 - 100, !"disconnect.requiresAuth".equals(this.errorMessage) ? this.height / 4 + 120 + 12 : this.height - this.height / 5 - 40, var1.translateKey("gui.toMenu")));
if(!"disconnect.requiresAuth".equals(this.errorMessage)) { if(!kickForDoS && !"disconnect.requiresAuth".equals(this.errorMessage)) {
if (this.field_74247_c != null) { if (this.field_74247_c != null) {
this.field_74245_d = this.fontRenderer.listFormattedStringToWidth(var1.translateKeyFormat(this.errorDetail, this.field_74247_c), this.width - 50); this.field_74245_d = this.fontRenderer.listFormattedStringToWidth(var1.translateKeyFormat(this.errorDetail, this.field_74247_c), this.width - 50);
} else { } else {
this.field_74245_d = this.fontRenderer.listFormattedStringToWidth(var1.translateKey(this.errorDetail), this.width - 50); this.field_74245_d = this.fontRenderer.listFormattedStringToWidth(var1.translateKey(this.errorDetail), this.width - 50);
} }
} }
int i = 0;
if(kickForDoS) {
this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 2 + this.height / 7, var1.translateKey("gui.toMenu")));
}else if("disconnect.requiresAuth".equals(this.errorMessage)){
this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 4 + 120 + 12, var1.translateKey("gui.toMenu")));
}else {
this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height - this.height / 5 - 40, var1.translateKey("gui.toMenu")));
}
} }
/** /**
@ -65,7 +91,19 @@ public class GuiDisconnected extends GuiScreen {
this.drawDefaultBackground(); this.drawDefaultBackground();
int var4 = this.height / 2 - 30; int var4 = this.height / 2 - 30;
if("disconnect.requiresAuth".equals(this.errorMessage)) {//22w12a if(kickForDoS) {
var4 -= 20;
this.drawCenteredString(this.fontRenderer, this.errorMessage, this.width / 2, var4 - 20, 11184810);
this.drawCenteredString(this.fontRenderer, this.errorDetail, this.width / 2, var4 + 10, 16777215);
String s = this.errorDetail2;
boolean b = s.startsWith("$");
if(b) {
s = s.substring(1);
var4 -= 2;
}
this.drawCenteredString(this.fontRenderer, s, this.width / 2, var4 + 24, b ? 16777215 : 0xFF5555);
this.drawCenteredString(this.fontRenderer, this.errorDetailTryAgain, this.width / 2, var4 + 50, 0x777777);
}else if("disconnect.requiresAuth".equals(this.errorMessage)) {//22w12a
EaglerAdapter.glPushMatrix(); EaglerAdapter.glPushMatrix();
EaglerAdapter.glScalef(1.5f, 1.5f, 1.5f); EaglerAdapter.glScalef(1.5f, 1.5f, 1.5f);
this.drawCenteredString(this.fontRenderer, "Authentication Required", this.width / 3, this.height / 4 - 30, 0xDD5555); this.drawCenteredString(this.fontRenderer, "Authentication Required", this.width / 3, this.height / 4 - 30, 0xDD5555);

View File

@ -79,7 +79,7 @@ public class GuiEnchantment extends GuiContainer {
EaglerAdapter.glPushMatrix(); EaglerAdapter.glPushMatrix();
EaglerAdapter.glLoadIdentity(); EaglerAdapter.glLoadIdentity();
ScaledResolution var6 = new ScaledResolution(this.mc.gameSettings, this.mc.displayWidth, this.mc.displayHeight); ScaledResolution var6 = new ScaledResolution(this.mc.gameSettings, this.mc.displayWidth, this.mc.displayHeight);
EaglerAdapter.glViewport((var6.getScaledWidth() - 320) / 2 * var6.getScaleFactor(), (var6.getScaledHeight() - 240) / 2 * var6.getScaleFactor(), 320 * var6.getScaleFactor(), 240 * var6.getScaleFactor()); EaglerAdapter.glViewport((var6.getScaledWidth() - 432) / 2 * var6.getScaleFactor(), (var6.getScaledHeight() - 182) / 2 * var6.getScaleFactor(), 320 * var6.getScaleFactor(), 240 * var6.getScaleFactor());
EaglerAdapter.glTranslatef(-0.34F, 0.23F, 0.0F); EaglerAdapter.glTranslatef(-0.34F, 0.23F, 0.0F);
EaglerAdapter.gluPerspective(90.0F, 1.3333334F, 9.0F, 80.0F); EaglerAdapter.gluPerspective(90.0F, 1.3333334F, 9.0F, 80.0F);
float var7 = 1.0F; float var7 = 1.0F;

View File

@ -20,7 +20,7 @@ public class GuiMultiplayer extends GuiScreen {
/** Slot container for the server list */ /** Slot container for the server list */
private GuiSlotServer serverSlotContainer; private GuiSlotServer serverSlotContainer;
private ServerList internetServerList; private static ServerList internetServerList = null;
/** Index of the currently selected server */ /** Index of the currently selected server */
private int selectedServer = -1; private int selectedServer = -1;
@ -49,14 +49,48 @@ public class GuiMultiplayer extends GuiScreen {
/** Instance of ServerData. */ /** Instance of ServerData. */
private ServerData theServerData = null; private ServerData theServerData = null;
private boolean hasInitialRefresh = false;
/** How many ticks this Gui is already opened */ /** How many ticks this Gui is already opened */
private int ticksOpened; private int ticksOpened;
private boolean field_74024_A;
private List listofLanServers = Collections.emptyList(); private List listofLanServers = Collections.emptyList();
private static long lastCooldown = 0l;
private static long lastRefresh = 0l;
private static int cooldownTimer = 0;
private static boolean isLockedOut = false;
public GuiMultiplayer(GuiScreen par1GuiScreen) { public GuiMultiplayer(GuiScreen par1GuiScreen) {
this.parentScreen = par1GuiScreen; this.parentScreen = par1GuiScreen;
isLockedOut = false;
}
public static void tickRefreshCooldown() {
if(cooldownTimer > 0) {
long t = System.currentTimeMillis();
if(t - lastCooldown > 5000l) {
--cooldownTimer;
lastCooldown = t;
}
}
}
private static boolean testIfCanRefresh() {
long t = System.currentTimeMillis();
if(t - lastRefresh > 1000l) {
lastRefresh = t;
if(cooldownTimer < 8) {
++cooldownTimer;
}else {
isLockedOut = true;
}
if(cooldownTimer < 5) {
isLockedOut = false;
return true;
}
}
return false;
} }
/** /**
@ -66,12 +100,15 @@ public class GuiMultiplayer extends GuiScreen {
EaglerAdapter.enableRepeatEvents(true); EaglerAdapter.enableRepeatEvents(true);
this.buttonList.clear(); this.buttonList.clear();
if (!this.field_74024_A) { if (!hasInitialRefresh) {
this.field_74024_A = true; hasInitialRefresh = true;
this.internetServerList = new ServerList(this.mc); if(internetServerList == null) {
this.internetServerList.loadServerList(); internetServerList = new ServerList(this.mc);
}else {
if(testIfCanRefresh()) {
internetServerList.loadServerList();
}
}
this.serverSlotContainer = new GuiSlotServer(this); this.serverSlotContainer = new GuiSlotServer(this);
} else { } else {
this.serverSlotContainer.func_77207_a(this.width, this.height, 32, this.height - 64); this.serverSlotContainer.func_77207_a(this.width, this.height, 32, this.height - 64);
@ -112,10 +149,6 @@ public class GuiMultiplayer extends GuiScreen {
*/ */
public void onGuiClosed() { public void onGuiClosed() {
EaglerAdapter.enableRepeatEvents(false); EaglerAdapter.enableRepeatEvents(false);
if(field_74024_A) {
internetServerList.freeServerIcons();
field_74024_A = false;
}
} }
/** /**
@ -154,7 +187,11 @@ public class GuiMultiplayer extends GuiScreen {
} else if (par1GuiButton.id == 0) { } else if (par1GuiButton.id == 0) {
this.mc.displayGuiScreen(this.parentScreen); this.mc.displayGuiScreen(this.parentScreen);
} else if (par1GuiButton.id == 8) { } else if (par1GuiButton.id == 8) {
this.mc.displayGuiScreen(new GuiMultiplayer(this.parentScreen)); if(testIfCanRefresh()) {
lastRefresh = 0;
--cooldownTimer;
this.mc.displayGuiScreen(new GuiMultiplayer(this.parentScreen));
}
} else { } else {
this.serverSlotContainer.actionPerformed(par1GuiButton); this.serverSlotContainer.actionPerformed(par1GuiButton);
} }
@ -166,8 +203,8 @@ public class GuiMultiplayer extends GuiScreen {
this.deleteClicked = false; this.deleteClicked = false;
if (par1) { if (par1) {
this.internetServerList.removeServerData(par2); internetServerList.removeServerData(par2);
this.internetServerList.saveServerList(); internetServerList.saveServerList();
this.selectedServer = -1; this.selectedServer = -1;
} }
@ -184,8 +221,8 @@ public class GuiMultiplayer extends GuiScreen {
this.addClicked = false; this.addClicked = false;
if (par1) { if (par1) {
this.internetServerList.addServerData(this.theServerData); internetServerList.addServerData(this.theServerData);
this.internetServerList.saveServerList(); internetServerList.saveServerList();
this.selectedServer = -1; this.selectedServer = -1;
} }
@ -257,6 +294,16 @@ public class GuiMultiplayer extends GuiScreen {
if (this.lagTooltip != null) { if (this.lagTooltip != null) {
this.func_74007_a(this.lagTooltip, par1, par2); this.func_74007_a(this.lagTooltip, par1, par2);
} }
if(isLockedOut) {
String canYouNot = "can you not";
int w = this.fontRenderer.getStringWidth(canYouNot);
drawRect((this.width - w - 4) / 2, this.height - 80, (this.width + w + 4) / 2, this.height - 70, 0xCC000000);
fontRenderer.drawStringWithShadow(canYouNot, (this.width - w) / 2, this.height - 79, 0xFFDD2222);
if(cooldownTimer < 3) {
isLockedOut = false;
}
}
} }
/** /**

View File

@ -80,7 +80,7 @@ class GuiSlotServer extends GuiSlot {
boolean var8 = var6.field_82821_f < 61; boolean var8 = var6.field_82821_f < 61;
boolean var9 = var7 || var8; boolean var9 = var7 || var8;
this.parentGui.drawString(this.parentGui.fontRenderer, var6.serverName, par2 + 38, par3 + 1, 16777215); this.parentGui.drawString(this.parentGui.fontRenderer, var6.serverName, par2 + 38, par3 + 1, 16777215);
if(var6.hasPing && var6.pingToServer > 0) { if(var6.hasPing && (var6.pingToServer > 0 || var6.hasError)) {
int i = var6.serverMOTD.indexOf('\n'); int i = var6.serverMOTD.indexOf('\n');
if(i > 0) { if(i > 0) {
this.parentGui.drawString(this.parentGui.fontRenderer, var6.serverMOTD.substring(0, i), par2 + 38, par3 + 12, 8421504); this.parentGui.drawString(this.parentGui.fontRenderer, var6.serverMOTD.substring(0, i), par2 + 38, par3 + 12, 8421504);

View File

@ -14,6 +14,7 @@ public class ModelBiped extends ModelBase {
public ModelRenderer bipedLeftLeg; public ModelRenderer bipedLeftLeg;
public ModelRenderer bipedEars; public ModelRenderer bipedEars;
public ModelRenderer bipedCloak; public ModelRenderer bipedCloak;
public boolean blockTransparentSkin = false;
/** /**
* Records whether the model should be rendered holding an item in the left * Records whether the model should be rendered holding an item in the left
@ -91,9 +92,13 @@ public class ModelBiped extends ModelBase {
EaglerAdapter.glPushMatrix(); EaglerAdapter.glPushMatrix();
EaglerAdapter.glScalef(1.0F / var8, 1.0F / var8, 1.0F / var8); EaglerAdapter.glScalef(1.0F / var8, 1.0F / var8, 1.0F / var8);
EaglerAdapter.glTranslatef(0.0F, 24.0F * par7, 0.0F); EaglerAdapter.glTranslatef(0.0F, 24.0F * par7, 0.0F);
if(par1Entity instanceof EntityPlayer && DefaultSkinRenderer.getPlayerRenderer((EntityPlayer)par1Entity) <= 0) EaglerAdapter.glDisable(EaglerAdapter.GL_ALPHA_TEST); if(blockTransparentSkin && par1Entity instanceof EntityPlayer && DefaultSkinRenderer.getPlayerRenderer((EntityPlayer)par1Entity) <= 0) {
EaglerAdapter.glDisable(EaglerAdapter.GL_ALPHA_TEST);
}
this.bipedBody.render(par7); this.bipedBody.render(par7);
if(par1Entity instanceof EntityPlayer && DefaultSkinRenderer.getPlayerRenderer((EntityPlayer)par1Entity) <= 0) EaglerAdapter.glEnable(EaglerAdapter.GL_ALPHA_TEST); if(blockTransparentSkin && par1Entity instanceof EntityPlayer && DefaultSkinRenderer.getPlayerRenderer((EntityPlayer)par1Entity) <= 0) {
EaglerAdapter.glEnable(EaglerAdapter.GL_ALPHA_TEST);
}
this.bipedRightArm.render(par7); this.bipedRightArm.render(par7);
this.bipedLeftArm.render(par7); this.bipedLeftArm.render(par7);
this.bipedRightLeg.render(par7); this.bipedRightLeg.render(par7);
@ -102,9 +107,13 @@ public class ModelBiped extends ModelBase {
EaglerAdapter.glPopMatrix(); EaglerAdapter.glPopMatrix();
} else { } else {
this.bipedHead.render(par7); this.bipedHead.render(par7);
if(par1Entity instanceof EntityPlayer && DefaultSkinRenderer.getPlayerRenderer((EntityPlayer)par1Entity) <= 0) EaglerAdapter.glDisable(EaglerAdapter.GL_ALPHA_TEST); if(blockTransparentSkin && par1Entity instanceof EntityPlayer && DefaultSkinRenderer.getPlayerRenderer((EntityPlayer)par1Entity) <= 0) {
EaglerAdapter.glDisable(EaglerAdapter.GL_ALPHA_TEST);
}
this.bipedBody.render(par7); this.bipedBody.render(par7);
if(par1Entity instanceof EntityPlayer && DefaultSkinRenderer.getPlayerRenderer((EntityPlayer)par1Entity) <= 0) EaglerAdapter.glEnable(EaglerAdapter.GL_ALPHA_TEST); if(blockTransparentSkin && par1Entity instanceof EntityPlayer && DefaultSkinRenderer.getPlayerRenderer((EntityPlayer)par1Entity) <= 0) {
EaglerAdapter.glEnable(EaglerAdapter.GL_ALPHA_TEST);
}
this.bipedRightArm.render(par7); this.bipedRightArm.render(par7);
this.bipedLeftArm.render(par7); this.bipedLeftArm.render(par7);
this.bipedRightLeg.render(par7); this.bipedRightLeg.render(par7);

View File

@ -34,10 +34,9 @@ public class MouseHelper {
* screen * screen
*/ */
public void ungrabMouseCursor() { public void ungrabMouseCursor() {
int var1 = Minecraft.getMinecraft().displayWidth; //int var1 = Minecraft.getMinecraft().displayWidth;
int var2 = Minecraft.getMinecraft().displayHeight; //int var2 = Minecraft.getMinecraft().displayHeight;
//EaglerAdapter.mouseSetCursorPosition(var1 / 2, var2 / 2);
EaglerAdapter.mouseSetCursorPosition(var1 / 2, var2 / 2);
EaglerAdapter.mouseSetGrabbed(false); EaglerAdapter.mouseSetGrabbed(false);
} }

View File

@ -5,14 +5,17 @@ import java.io.DataInputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import net.lax1dude.eaglercraft.DefaultSkinRenderer; import net.lax1dude.eaglercraft.DefaultSkinRenderer;
import net.lax1dude.eaglercraft.EaglerAdapter; import net.lax1dude.eaglercraft.EaglerAdapter;
import net.lax1dude.eaglercraft.EaglercraftRandom; import net.lax1dude.eaglercraft.EaglercraftRandom;
import net.lax1dude.eaglercraft.WebsocketNetworkManager; import net.lax1dude.eaglercraft.WebsocketNetworkManager;
import net.lax1dude.eaglercraft.adapter.EaglerAdapterImpl2.RateLimit;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
public class NetClientHandler extends NetHandler { public class NetClientHandler extends NetHandler {
@ -79,16 +82,35 @@ public class NetClientHandler extends NetHandler {
* function. * function.
*/ */
public void processReadPackets() { public void processReadPackets() {
if (!this.disconnected && this.netManager != null) { if (!this.disconnected && this.netManager != null) {
this.netManager.processReadPackets(); this.netManager.processReadPackets();
} }
if(!EaglerAdapter.connectionOpen()) { if(!EaglerAdapter.connectionOpen()) {
this.mc.displayGuiScreen(new GuiDisconnected(new GuiMultiplayer(new GuiMainMenu()), "disconnect.disconnected", "disconnect.endOfStream", null)); if(!this.disconnected) {
this.disconnected = true; RateLimit r = EaglerAdapter.getRateLimitStatus();
this.mc.loadWorld((WorldClient) null); if(r != null) {
return; if(r == RateLimit.NOW_LOCKED) {
this.mc.displayGuiScreen(new GuiDisconnected(new GuiMultiplayer(new GuiMainMenu()), "disconnect.ratelimit.ipNowLocked", "disconnect.endOfStream", null));
}else if(r == RateLimit.LOCKED) {
this.mc.displayGuiScreen(new GuiDisconnected(new GuiMultiplayer(new GuiMainMenu()), "disconnect.ratelimit.ipLocked", "disconnect.endOfStream", null));
}else if(r == RateLimit.BLOCKED) {
this.mc.displayGuiScreen(new GuiDisconnected(new GuiMultiplayer(new GuiMainMenu()), "disconnect.ratelimit.ipBlocked", "disconnect.endOfStream", null));
}else if(r == RateLimit.FAILED_POSSIBLY_LOCKED) {
this.mc.displayGuiScreen(new GuiDisconnected(new GuiMultiplayer(new GuiMainMenu()), "disconnect.ratelimit.ipFailedPossiblyLocked", "disconnect.endOfStream", null));
}else {
this.mc.displayGuiScreen(new GuiDisconnected(new GuiMultiplayer(new GuiMainMenu()), "disconnect.disconnected", "RateLimit." + r.name(), null));
}
}else {
this.mc.displayGuiScreen(new GuiDisconnected(new GuiMultiplayer(new GuiMainMenu()), "disconnect.disconnected", "disconnect.endOfStream", null));
}
this.disconnected = true;
this.mc.loadWorld((WorldClient) null);
}
}else {
if(this.disconnected) {
EaglerAdapter.endConnection();
}
} }
} }
@ -458,32 +480,37 @@ public class NetClientHandler extends NetHandler {
this.netManager.networkShutdown("disconnect.kicked", new Object[0]); this.netManager.networkShutdown("disconnect.kicked", new Object[0]);
this.disconnected = true; this.disconnected = true;
this.mc.loadWorld((WorldClient) null); this.mc.loadWorld((WorldClient) null);
if(par1Packet255KickDisconnect.reason.equalsIgnoreCase("BLOCKED")) {
this.mc.displayGuiScreen(new GuiDisconnected(new GuiMultiplayer(new GuiMainMenu()), "disconnect.disconnected", "disconnect.genericReason", new Object[] { par1Packet255KickDisconnect.reason })); EaglerAdapter.logRateLimit(netManager.getServerURI(), RateLimit.BLOCKED);
this.mc.displayGuiScreen(new GuiDisconnected(new GuiMultiplayer(new GuiMainMenu()), "disconnect.ratelimit.kickBlocked", "disconnect.endOfStream", (Object[])null));
}else if(par1Packet255KickDisconnect.reason.equalsIgnoreCase("LOCKED")) {
EaglerAdapter.logRateLimit(netManager.getServerURI(), RateLimit.LOCKED);
this.mc.displayGuiScreen(new GuiDisconnected(new GuiMultiplayer(new GuiMainMenu()), "disconnect.ratelimit.kickLocked", "disconnect.endOfStream", (Object[])null));
}else {
this.mc.displayGuiScreen(new GuiDisconnected(new GuiMultiplayer(new GuiMainMenu()), "disconnect.disconnected", "disconnect.genericReason", new Object[] { par1Packet255KickDisconnect.reason }));
}
} }
public void handleErrorMessage(String par1Str, Object[] par2ArrayOfObj) { public void handleErrorMessage(String par1Str, Object[] par2ArrayOfObj) {
if (!this.disconnected) { if (!this.disconnected) {
this.disconnected = true; this.disconnected = true;
this.mc.loadWorld((WorldClient) null); this.mc.loadWorld((WorldClient) null);
this.mc.displayGuiScreen(new GuiDisconnected(new GuiMultiplayer(new GuiMainMenu()), "disconnect.lost", par1Str, par2ArrayOfObj)); this.mc.displayGuiScreen(new GuiDisconnected(new GuiMultiplayer(new GuiMainMenu()), "disconnect.lost", par1Str, par2ArrayOfObj));
} }
} }
public void quitWithPacket(Packet par1Packet) { public void quitWithPacket(Packet par1Packet) {
if (!this.disconnected) { if (!this.disconnected && EaglerAdapter.connectionOpen()) {
this.netManager.addToSendQueue(par1Packet); this.netManager.addToSendQueue(par1Packet);
this.netManager.serverShutdown();
} }
this.netManager.serverShutdown();
} }
/** /**
* Adds the packet to the send queue * Adds the packet to the send queue
*/ */
public void addToSendQueue(Packet par1Packet) { public void addToSendQueue(Packet par1Packet) {
if (!this.disconnected) { if (!this.disconnected && EaglerAdapter.connectionOpen()) {
this.netManager.addToSendQueue(par1Packet); this.netManager.addToSendQueue(par1Packet);
} }
} }

View File

@ -21,7 +21,9 @@ public class RenderItemFrame extends Render {
int var14 = par1EntityItemFrame.yPosition; int var14 = par1EntityItemFrame.yPosition;
int var15 = par1EntityItemFrame.zPosition + Direction.offsetZ[par1EntityItemFrame.hangingDirection]; int var15 = par1EntityItemFrame.zPosition + Direction.offsetZ[par1EntityItemFrame.hangingDirection];
EaglerAdapter.glTranslatef((float) var13 - var10, (float) var14 - var11, (float) var15 - var12); EaglerAdapter.glTranslatef((float) var13 - var10, (float) var14 - var11, (float) var15 - var12);
this.renderFrameItemAsBlock(par1EntityItemFrame); if (par1EntityItemFrame.getDisplayedItem() == null || par1EntityItemFrame.getDisplayedItem().getItem() != Item.map) {
this.renderFrameItemAsBlock(par1EntityItemFrame);
}
this.func_82402_b(par1EntityItemFrame); this.func_82402_b(par1EntityItemFrame);
EaglerAdapter.glPopMatrix(); EaglerAdapter.glPopMatrix();
} }
@ -104,9 +106,11 @@ public class RenderItemFrame extends Render {
Tessellator var4 = Tessellator.instance; Tessellator var4 = Tessellator.instance;
EaglerAdapter.glRotatef(180.0F, 0.0F, 1.0F, 0.0F); EaglerAdapter.glRotatef(180.0F, 0.0F, 1.0F, 0.0F);
EaglerAdapter.glRotatef(180.0F, 0.0F, 0.0F, 1.0F); EaglerAdapter.glRotatef(180.0F, 0.0F, 0.0F, 1.0F);
EaglerAdapter.glScalef(0.00390625F, 0.00390625F, 0.00390625F); //EaglerAdapter.glScalef(0.00390625F, 0.00390625F, 0.00390625F);
EaglerAdapter.glTranslatef(-65.0F, -107.0F, -3.0F); EaglerAdapter.glScalef(0.0078125F, 0.0078125F, 0.0078125F);
EaglerAdapter.glTranslatef(-65.0F, -85.0F, 1.0F);
EaglerAdapter.glNormal3f(0.0F, 0.0F, -1.0F); EaglerAdapter.glNormal3f(0.0F, 0.0F, -1.0F);
EaglerAdapter.glDisable(EaglerAdapter.GL_CULL_FACE);
var4.startDrawingQuads(); var4.startDrawingQuads();
byte var5 = 7; byte var5 = 7;
var4.addVertexWithUV((double) (0 - var5), (double) (128 + var5), 0.0D, 0.0D, 1.0D); var4.addVertexWithUV((double) (0 - var5), (double) (128 + var5), 0.0D, 0.0D, 1.0D);
@ -114,6 +118,8 @@ public class RenderItemFrame extends Render {
var4.addVertexWithUV((double) (128 + var5), (double) (0 - var5), 0.0D, 1.0D, 0.0D); var4.addVertexWithUV((double) (128 + var5), (double) (0 - var5), 0.0D, 1.0D, 0.0D);
var4.addVertexWithUV((double) (0 - var5), (double) (0 - var5), 0.0D, 0.0D, 0.0D); var4.addVertexWithUV((double) (0 - var5), (double) (0 - var5), 0.0D, 0.0D, 0.0D);
var4.draw(); var4.draw();
EaglerAdapter.glEnable(EaglerAdapter.GL_CULL_FACE);
EaglerAdapter.glTranslatef(0.0F, 0.0F, -2.0F);
MapData var6 = Item.map.getMapData(var3.getEntityItem(), par1EntityItemFrame.worldObj); MapData var6 = Item.map.getMapData(var3.getEntityItem(), par1EntityItemFrame.worldObj);
EaglerAdapter.glTranslatef(0.0F, 0.0F, -1.0F); EaglerAdapter.glTranslatef(0.0F, 0.0F, -1.0F);

View File

@ -135,7 +135,9 @@ public class RenderPlayer extends RenderLiving {
this.mainModel = (DefaultSkinRenderer.isPlayerNewSkin(par1EntityPlayer) ? (DefaultSkinRenderer.isPlayerNewSkinSlim(par1EntityPlayer) ? this.modelBipedMainNewSkinSlim : this.modelBipedMainNewSkin) : this.modelBipedMain); this.mainModel = (DefaultSkinRenderer.isPlayerNewSkin(par1EntityPlayer) ? (DefaultSkinRenderer.isPlayerNewSkinSlim(par1EntityPlayer) ? this.modelBipedMainNewSkinSlim : this.modelBipedMainNewSkin) : this.modelBipedMain);
this.mainModel.isChild = false; this.mainModel.isChild = false;
((ModelBiped)this.mainModel).blockTransparentSkin = true;
super.doRenderLiving(par1EntityPlayer, par2, var14, par6, par8, par9); super.doRenderLiving(par1EntityPlayer, par2, var14, par6, par8, par9);
((ModelBiped)this.mainModel).blockTransparentSkin = false;
//this.mainModel = this.modelBipedMain; //this.mainModel = this.modelBipedMain;
this.modelArmorChestplate.aimedBow = this.modelArmor.aimedBow = this.modelBipedMain.aimedBow = this.modelBipedMainNewSkin.aimedBow = this.modelBipedMainNewSkinSlim.aimedBow = false; this.modelArmorChestplate.aimedBow = this.modelArmor.aimedBow = this.modelBipedMain.aimedBow = this.modelBipedMainNewSkin.aimedBow = this.modelBipedMainNewSkinSlim.aimedBow = false;
this.modelArmorChestplate.isSneak = this.modelArmor.isSneak = this.modelBipedMain.isSneak = this.modelBipedMainNewSkin.isSneak = this.modelBipedMainNewSkinSlim.isSneak = false; this.modelArmorChestplate.isSneak = this.modelArmor.isSneak = this.modelBipedMain.isSneak = this.modelBipedMainNewSkin.isSneak = this.modelBipedMainNewSkinSlim.isSneak = false;

View File

@ -43,6 +43,7 @@ public class ServerData {
public int[] serverIcon = null; public int[] serverIcon = null;
public boolean serverIconDirty = false; public boolean serverIconDirty = false;
public boolean serverIconEnabled = false; public boolean serverIconEnabled = false;
public boolean hasError = false;
public List<String> playerList = new ArrayList(); public List<String> playerList = new ArrayList();
public int serverIconGL = -1; public int serverIconGL = -1;
@ -99,16 +100,6 @@ public class ServerData {
} }
public void setMOTDFromQuery(QueryResponse pkt) { public void setMOTDFromQuery(QueryResponse pkt) {
if(!pkt.serverCracked) {
this.gameVersion = "";
this.serverIconEnabled = false;
this.serverMOTD = "" + EnumChatFormatting.RED + "Error: account required";
this.populationInfo = "";
if(pingSentTime > 0l) {
pingToServer = pkt.clientTime - pingSentTime;
pingSentTime = 0l;
}
}
JSONObject motdData = pkt.getResponseJSON(); JSONObject motdData = pkt.getResponseJSON();
JSONArray motd = motdData.getJSONArray("motd"); JSONArray motd = motdData.getJSONArray("motd");
this.serverMOTD = motd.length() > 0 ? (motd.length() > 1 ? motd.getString(0) + "\n" + motd.getString(1) : motd.getString(0)) : ""; this.serverMOTD = motd.length() > 0 ? (motd.length() > 1 ? motd.getString(0) + "\n" + motd.getString(1) : motd.getString(0)) : "";
@ -125,7 +116,23 @@ public class ServerData {
serverIconGL = -1; serverIconGL = -1;
} }
} }
hasError = false;
}
public void setRateLimitError(boolean lock, boolean isTcp) {
if(lock) {
serverMOTD = EnumChatFormatting.RED + "Your IP is banned for DoS\n" + EnumChatFormatting.GRAY + "Try again in an hour";
}else {
if(isTcp) {
serverMOTD = EnumChatFormatting.RED + "Connection Blocked\n" + EnumChatFormatting.GRAY + "Try again in a minute";
}else {
serverMOTD = EnumChatFormatting.RED + "Query Was Blocked\n" + EnumChatFormatting.GRAY + "Try again in a minute";
}
}
this.populationInfo = "";
this.playerList.clear();
this.serverIconEnabled = false;
this.hasError = true;
} }
public void refreshIcon() { public void refreshIcon() {

View File

@ -2,13 +2,16 @@ package net.minecraft.src;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import net.lax1dude.eaglercraft.Base64; import net.lax1dude.eaglercraft.Base64;
import net.lax1dude.eaglercraft.ConfigConstants; import net.lax1dude.eaglercraft.ConfigConstants;
import net.lax1dude.eaglercraft.EaglerAdapter; import net.lax1dude.eaglercraft.EaglerAdapter;
import net.lax1dude.eaglercraft.LocalStorageManager; import net.lax1dude.eaglercraft.LocalStorageManager;
import net.lax1dude.eaglercraft.ServerQuery.QueryResponse; import net.lax1dude.eaglercraft.ServerQuery.QueryResponse;
import net.lax1dude.eaglercraft.adapter.EaglerAdapterImpl2.RateLimit;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
public class ServerList { public class ServerList {
@ -19,6 +22,7 @@ public class ServerList {
private final List<ServerData> servers = new ArrayList(); private final List<ServerData> servers = new ArrayList();
public static final List<ServerData> forcedServers = new ArrayList(); public static final List<ServerData> forcedServers = new ArrayList();
private static final Set<String> motdLocks = new HashSet();
public ServerList(Minecraft par1Minecraft) { public ServerList(Minecraft par1Minecraft) {
this.mc = par1Minecraft; this.mc = par1Minecraft;
@ -176,11 +180,22 @@ public class ServerList {
do { do {
pkt = dat.currentQuery.getResponse(); pkt = dat.currentQuery.getResponse();
}while(dat.currentQuery.responseAvailable() > 0); }while(dat.currentQuery.responseAvailable() > 0);
if(pkt.responseType.equalsIgnoreCase("MOTD") && pkt.isResponseJSON()) { if(pkt.rateLimitStatus != null) {
dat.setMOTDFromQuery(pkt); if(pkt.rateLimitStatus == RateLimit.LOCKED) {
if(!dat.hasPing) { dat.setRateLimitError(true, pkt.rateLimitIsTCP);
dat.pingToServer = pkt.clientTime - dat.pingSentTime; }else if(pkt.rateLimitStatus == RateLimit.BLOCKED) {
dat.hasPing = true; dat.setRateLimitError(false, pkt.rateLimitIsTCP);
}
dat.currentQuery.close();
dat.pingToServer = -1l;
dat.hasPing = true;
}else {
if(pkt.responseType.equalsIgnoreCase("MOTD") && pkt.isResponseJSON()) {
dat.setMOTDFromQuery(pkt);
if(!dat.hasPing) {
dat.pingToServer = pkt.clientTime - dat.pingSentTime;
dat.hasPing = true;
}
} }
} }
} }

View File

@ -10,7 +10,9 @@ import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Set;
import org.json.JSONObject; import org.json.JSONObject;
import org.teavm.interop.Async; import org.teavm.interop.Async;
@ -62,7 +64,7 @@ import net.lax1dude.eaglercraft.ServerQuery.QueryResponse;
import net.lax1dude.eaglercraft.adapter.teavm.WebGLQuery; import net.lax1dude.eaglercraft.adapter.teavm.WebGLQuery;
import net.lax1dude.eaglercraft.adapter.teavm.WebGLVertexArray; import net.lax1dude.eaglercraft.adapter.teavm.WebGLVertexArray;
import net.minecraft.src.MathHelper; import net.minecraft.src.MathHelper;
import net.lax1dude.eaglercraft.adapter.EaglerAdapterImpl2.RateLimit;
import net.lax1dude.eaglercraft.adapter.teavm.WebGL2RenderingContext; import net.lax1dude.eaglercraft.adapter.teavm.WebGL2RenderingContext;
import static net.lax1dude.eaglercraft.adapter.teavm.WebGL2RenderingContext.*; import static net.lax1dude.eaglercraft.adapter.teavm.WebGL2RenderingContext.*;
@ -184,6 +186,9 @@ public class EaglerAdapterImpl2 {
@JSBody(params = { "m" }, script = "return m.offsetY;") @JSBody(params = { "m" }, script = "return m.offsetY;")
private static native int getOffsetY(MouseEvent m); private static native int getOffsetY(MouseEvent m);
@JSBody(params = { "e" }, script = "return e.which;")
private static native int getWhich(KeyboardEvent e);
public static final void initializeContext(HTMLElement rootElement, String assetPackageURI) { public static final void initializeContext(HTMLElement rootElement, String assetPackageURI) {
parent = rootElement; parent = rootElement;
String s = parent.getAttribute("style"); String s = parent.getAttribute("style");
@ -245,7 +250,8 @@ public class EaglerAdapterImpl2 {
win.addEventListener("keydown", keydown = new EventListener<KeyboardEvent>() { win.addEventListener("keydown", keydown = new EventListener<KeyboardEvent>() {
@Override @Override
public void handleEvent(KeyboardEvent evt) { public void handleEvent(KeyboardEvent evt) {
keyStates[remapKey(evt.getKeyCode())] = true; //keyStates[remapKey(evt.getKeyCode())] = true;
keyStates[remapKey(getWhich(evt))] = true;
keyEvents.add(evt); keyEvents.add(evt);
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
@ -254,7 +260,8 @@ public class EaglerAdapterImpl2 {
win.addEventListener("keyup", keyup = new EventListener<KeyboardEvent>() { win.addEventListener("keyup", keyup = new EventListener<KeyboardEvent>() {
@Override @Override
public void handleEvent(KeyboardEvent evt) { public void handleEvent(KeyboardEvent evt) {
keyStates[remapKey(evt.getKeyCode())] = false; //keyStates[remapKey(evt.getKeyCode())] = false;
keyStates[remapKey(getWhich(evt))] = false;
keyEvents.add(evt); keyEvents.add(evt);
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
@ -945,7 +952,7 @@ public class EaglerAdapterImpl2 {
return !keyEvents.isEmpty() && (currentEventK = keyEvents.remove(0)) != null; return !keyEvents.isEmpty() && (currentEventK = keyEvents.remove(0)) != null;
} }
public static final int getEventKey() { public static final int getEventKey() {
return currentEventK == null ? -1 : remapKey(currentEventK.getKeyCode()); return currentEventK == null ? -1 : remapKey(getWhich(currentEventK));
} }
public static final char getEventChar() { public static final char getEventChar() {
if(currentEventK == null) return '\0'; if(currentEventK == null) return '\0';
@ -1039,19 +1046,57 @@ public class EaglerAdapterImpl2 {
@JSBody(params = { "name", "cvs" }, script = "var a=document.createElement(\"a\");a.href=cvs.toDataURL(\"image/png\");a.download=name;a.click();") @JSBody(params = { "name", "cvs" }, script = "var a=document.createElement(\"a\");a.href=cvs.toDataURL(\"image/png\");a.download=name;a.click();")
private static native void saveScreenshot(String name, HTMLCanvasElement cvs); private static native void saveScreenshot(String name, HTMLCanvasElement cvs);
public static enum RateLimit {
NONE, FAILED, BLOCKED, FAILED_POSSIBLY_LOCKED, LOCKED, NOW_LOCKED;
}
private static final Set<String> rateLimitedAddresses = new HashSet();
private static final Set<String> blockedAddresses = new HashSet();
private static WebSocket sock = null; private static WebSocket sock = null;
private static boolean sockIsConnecting = false; private static boolean sockIsConnecting = false;
private static boolean sockIsConnected = false;
private static boolean sockIsAlive = false;
private static LinkedList<byte[]> readPackets = new LinkedList(); private static LinkedList<byte[]> readPackets = new LinkedList();
private static RateLimit rateLimitStatus = null;
private static String currentSockURI = null;
public static final RateLimit getRateLimitStatus() {
RateLimit l = rateLimitStatus;
rateLimitStatus = null;
return l;
}
public static final void logRateLimit(String addr, RateLimit l) {
if(l == RateLimit.BLOCKED) {
blockedAddresses.add(addr);
}else {
rateLimitedAddresses.add(addr);
}
}
public static final RateLimit checkRateLimitHistory(String addr) {
if(blockedAddresses.contains(addr)) {
return RateLimit.LOCKED;
}else if(rateLimitedAddresses.contains(addr)) {
return RateLimit.BLOCKED;
}else {
return RateLimit.NONE;
}
}
@Async @Async
public static native String connectWebSocket(String sockURI); public static native String connectWebSocket(String sockURI);
private static void connectWebSocket(String sockURI, final AsyncCallback<String> cb) { private static void connectWebSocket(String sockURI, final AsyncCallback<String> cb) {
sockIsConnecting = true; sockIsConnecting = true;
sockIsConnected = false;
sockIsAlive = false;
rateLimitStatus = null;
currentSockURI = sockURI;
try { try {
sock = WebSocket.create(sockURI); sock = WebSocket.create(sockURI);
} catch(Throwable t) { } catch(Throwable t) {
sockIsConnecting = false; sockIsConnecting = false;
sockIsAlive = false;
return; return;
} }
sock.setBinaryType("arraybuffer"); sock.setBinaryType("arraybuffer");
@ -1059,6 +1104,8 @@ public class EaglerAdapterImpl2 {
@Override @Override
public void handleEvent(MessageEvent evt) { public void handleEvent(MessageEvent evt) {
sockIsConnecting = false; sockIsConnecting = false;
sockIsAlive = false;
sockIsConnected = true;
readPackets.clear(); readPackets.clear();
cb.complete("okay"); cb.complete("okay");
} }
@ -1067,16 +1114,55 @@ public class EaglerAdapterImpl2 {
@Override @Override
public void handleEvent(CloseEvent evt) { public void handleEvent(CloseEvent evt) {
sock = null; sock = null;
readPackets.clear(); if(sockIsConnecting) {
if(rateLimitStatus == null) {
if(blockedAddresses.contains(currentSockURI)) {
rateLimitStatus = RateLimit.LOCKED;
}else if(rateLimitedAddresses.contains(currentSockURI)) {
rateLimitStatus = RateLimit.FAILED_POSSIBLY_LOCKED;
}else {
rateLimitStatus = RateLimit.FAILED;
}
}
}else if(!sockIsAlive) {
if(rateLimitStatus == null) {
if(blockedAddresses.contains(currentSockURI)) {
rateLimitStatus = RateLimit.LOCKED;
}else if(rateLimitedAddresses.contains(currentSockURI)) {
rateLimitStatus = RateLimit.BLOCKED;
}
}
}
boolean b = sockIsConnecting; boolean b = sockIsConnecting;
sockIsConnecting = false; sockIsConnecting = false;
sockIsConnected = false;
sockIsAlive = false;
if(b) cb.complete("fail"); if(b) cb.complete("fail");
} }
}); });
sock.onMessage(new EventListener<MessageEvent>() { sock.onMessage(new EventListener<MessageEvent>() {
@Override @Override
public void handleEvent(MessageEvent evt) { public void handleEvent(MessageEvent evt) {
if(isString(evt.getData())) return; sockIsAlive = true;
if(isString(evt.getData())) {
String stat = evt.getDataAsString();
if(stat.equalsIgnoreCase("BLOCKED")) {
if(rateLimitStatus == null) {
rateLimitStatus = RateLimit.BLOCKED;
}
rateLimitedAddresses.add(currentSockURI);
}else if(stat.equalsIgnoreCase("LOCKED")) {
if(rateLimitStatus == null) {
rateLimitStatus = RateLimit.NOW_LOCKED;
}
rateLimitedAddresses.add(currentSockURI);
blockedAddresses.add(currentSockURI);
}
sockIsConnecting = false;
sockIsConnected = false;
sock.close();
return;
}
Uint8Array a = Uint8Array.create(evt.getDataAsArray()); Uint8Array a = Uint8Array.create(evt.getDataAsArray());
byte[] b = new byte[a.getByteLength()]; byte[] b = new byte[a.getByteLength()];
for(int i = 0; i < b.length; ++i) { for(int i = 0; i < b.length; ++i) {
@ -1092,10 +1178,16 @@ public class EaglerAdapterImpl2 {
return "fail".equals(res) ? false : true; return "fail".equals(res) ? false : true;
} }
public static final void endConnection() { public static final void endConnection() {
if(sock == null || sock.getReadyState() == 3) {
sockIsConnecting = false;
}
if(sock != null && !sockIsConnecting) sock.close(); if(sock != null && !sockIsConnecting) sock.close();
} }
public static final boolean connectionOpen() { public static final boolean connectionOpen() {
return sock != null && !sockIsConnecting; if(sock == null || sock.getReadyState() == 3) {
sockIsConnecting = false;
}
return sock != null && !sockIsConnecting && sock.getReadyState() != 3;
} }
@JSBody(params = { "sock", "buffer" }, script = "sock.send(buffer);") @JSBody(params = { "sock", "buffer" }, script = "sock.send(buffer);")
private static native void nativeBinarySend(WebSocket sock, ArrayBuffer buffer); private static native void nativeBinarySend(WebSocket sock, ArrayBuffer buffer);
@ -1659,11 +1751,15 @@ public class EaglerAdapterImpl2 {
private final LinkedList<byte[]> queryResponsesBytes = new LinkedList(); private final LinkedList<byte[]> queryResponsesBytes = new LinkedList();
private final String type; private final String type;
private boolean open; private boolean open;
private boolean alive;
private String uriString;
private final WebSocket sock; private final WebSocket sock;
private ServerQueryImpl(String type_, String uri) { private ServerQueryImpl(String type_, String uri) {
type = type_; type = type_;
uriString = uri;
alive = false;
WebSocket s = null; WebSocket s = null;
try { try {
s = WebSocket.create(uri); s = WebSocket.create(uri);
@ -1671,6 +1767,13 @@ public class EaglerAdapterImpl2 {
open = true; open = true;
}catch(Throwable t) { }catch(Throwable t) {
open = false; open = false;
if(EaglerAdapterImpl2.blockedAddresses.contains(uriString)) {
queryResponses.add(new QueryResponse(true));
}else if(EaglerAdapterImpl2.rateLimitedAddresses.contains(uriString)) {
queryResponses.add(new QueryResponse(false));
}
sock = null;
return;
} }
sock = s; sock = s;
if(open) { if(open) {
@ -1684,14 +1787,44 @@ public class EaglerAdapterImpl2 {
@Override @Override
public void handleEvent(CloseEvent evt) { public void handleEvent(CloseEvent evt) {
open = false; open = false;
if(!alive) {
if(EaglerAdapterImpl2.blockedAddresses.contains(uriString)) {
queryResponses.add(new QueryResponse(true));
}else if(EaglerAdapterImpl2.rateLimitedAddresses.contains(uriString)) {
queryResponses.add(new QueryResponse(false));
}
}
} }
}); });
sock.onMessage(new EventListener<MessageEvent>() { sock.onMessage(new EventListener<MessageEvent>() {
@Override @Override
public void handleEvent(MessageEvent evt) { public void handleEvent(MessageEvent evt) {
alive = true;
if(isString(evt.getData())) { if(isString(evt.getData())) {
try { try {
queryResponses.add(new QueryResponse(new JSONObject(evt.getDataAsString()))); String str = evt.getDataAsString();
if(str.equalsIgnoreCase("BLOCKED")) {
EaglerAdapterImpl2.rateLimitedAddresses.add(uriString);
queryResponses.add(new QueryResponse(false));
sock.close();
return;
}else if(str.equalsIgnoreCase("LOCKED")) {
EaglerAdapterImpl2.blockedAddresses.add(uriString);
queryResponses.add(new QueryResponse(true));
sock.close();
return;
}else {
QueryResponse q = new QueryResponse(new JSONObject(str));
if(q.rateLimitStatus != null) {
if(q.rateLimitStatus == RateLimit.BLOCKED) {
EaglerAdapterImpl2.rateLimitedAddresses.add(uriString);
}else if(q.rateLimitStatus == RateLimit.LOCKED) {
EaglerAdapterImpl2.blockedAddresses.add(uriString);
}
sock.close();
}
queryResponses.add(q);
}
}catch(Throwable t) { }catch(Throwable t) {
System.err.println("Query response could not be parsed: " + t.toString()); System.err.println("Query response could not be parsed: " + t.toString());
} }

File diff suppressed because one or more lines are too long

View File

@ -39,7 +39,7 @@ replace-bukkit=false
[Wildcards] [Wildcards]
# *fuck* # *fuck*
# nigg* # shi*
[Regex] [Regex]

View File

@ -1,6 +1,5 @@
listeners: listeners:
- motd: '&6An Eaglercraft server' - fallback_server: lobby
fallback_server: lobby
tab_list: GLOBAL_PING tab_list: GLOBAL_PING
websocket: true websocket: true
texture_size: 16 texture_size: 16
@ -12,9 +11,46 @@ listeners:
force_default_server: true force_default_server: true
forward_ip: false forward_ip: false
server_icon: server-icon.png server_icon: server-icon.png
motd1: '&6An Eaglercraft server'
allow_motd: true
allow_query: true
request_motd_cache:
cache_ttl: 7200
online_server_list_animation: false
online_server_list_results: true
online_server_list_trending: true
online_server_list_portfolios: false
ratelimit:
ip:
enable: true
period: 90
limit: 60
limit_lockout: 80
lockout_duration: 1200
exceptions: []
login:
enable: true
period: 50
limit: 5
limit_lockout: 10
lockout_duration: 300
exceptions: []
motd:
enable: true
period: 30
limit: 5
limit_lockout: 15
lockout_duration: 300
exceptions: []
query:
enable: true
period: 30
limit: 15
limit_lockout: 25
lockout_duration: 900
exceptions: []
player_limit: -1 player_limit: -1
timeout: 30000 timeout: 30000
stats: 595698b3-9c36-4e86-b1ee-cb3027038f41
servers: servers:
lobby: lobby:
address: localhost:25569 address: localhost:25569
@ -37,4 +73,5 @@ permissions:
- bungeecord.command.eag.banned - bungeecord.command.eag.banned
- bungeecord.command.eag.banlist - bungeecord.command.eag.banlist
- bungeecord.command.eag.unban - bungeecord.command.eag.unban
- bungeecord.command.eag.ratelimit
groups: {} groups: {}

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long