mirror of
https://github.com/Eaglercraft-Archive/Eaglercraftx-1.8.8-src.git
synced 2025-06-28 10:58:15 -05:00
(1.3.0) Add protocol V4 support to EaglerXBungee
This commit is contained in:
@ -4,6 +4,7 @@ import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.logging.Level;
|
||||
@ -15,7 +16,10 @@ import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.EaglerBackendRPCProtocol;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.auth.DefaultAuthSystem;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.command.CommandClientBrand;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.command.CommandConfirmCode;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.command.CommandDomain;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.command.CommandEaglerPurge;
|
||||
@ -27,6 +31,7 @@ import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerList
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.handlers.EaglerPacketEventListener;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.handlers.EaglerPluginEventListener;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerPipeline;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerUpdateSvc;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.web.HttpWebServer;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.shit.CompatWarning;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.BinaryHttpClient;
|
||||
@ -35,7 +40,9 @@ import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.ISkinServic
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SkinService;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SkinServiceOffline;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.voice.VoiceService;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.api.plugin.PluginDescription;
|
||||
import net.md_5.bungee.api.plugin.PluginManager;
|
||||
import net.md_5.bungee.netty.PipelineUtils;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
@ -57,7 +64,7 @@ import net.md_5.bungee.BungeeCord;
|
||||
*/
|
||||
public class EaglerXBungee extends Plugin {
|
||||
|
||||
public static final String NATIVE_BUNGEECORD_BUILD = "1.21-R0.1-SNAPSHOT:cda4537:1851";
|
||||
public static final String NATIVE_BUNGEECORD_BUILD = "1.21-R0.1-SNAPSHOT:acb85e3:1871";
|
||||
public static final String NATIVE_WATERFALL_BUILD = "1.21-R0.1-SNAPSHOT:de8345a:579";
|
||||
|
||||
static {
|
||||
@ -72,6 +79,7 @@ public class EaglerXBungee extends Plugin {
|
||||
private Timer closeInactiveConnections = null;
|
||||
private Timer skinServiceTasks = null;
|
||||
private Timer authServiceTasks = null;
|
||||
private Timer updateServiceTasks = null;
|
||||
private final ChannelFutureListener newChannelListener;
|
||||
private ISkinService skinService;
|
||||
private CapeServiceOffline capeService;
|
||||
@ -80,7 +88,7 @@ public class EaglerXBungee extends Plugin {
|
||||
|
||||
public EaglerXBungee() {
|
||||
instance = this;
|
||||
openChannels = new LinkedList();
|
||||
openChannels = new LinkedList<>();
|
||||
newChannelListener = new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture ch) throws Exception {
|
||||
@ -99,6 +107,12 @@ public class EaglerXBungee extends Plugin {
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
Map<String, String> templateGlobals = EaglerXBungeeAPIHelper.getTemplateGlobals();
|
||||
PluginDescription desc = this.getDescription();
|
||||
templateGlobals.put("plugin_name", desc.getName());
|
||||
templateGlobals.put("plugin_version", desc.getVersion());
|
||||
templateGlobals.put("plugin_authors", desc.getAuthor());
|
||||
templateGlobals.put("plugin_description", desc.getDescription());
|
||||
try {
|
||||
eventLoopGroup = ((BungeeCord) getProxy()).eventLoops;
|
||||
} catch (NoSuchFieldError e) {
|
||||
@ -120,6 +134,7 @@ public class EaglerXBungee extends Plugin {
|
||||
mgr.registerCommand(this, new CommandRatelimit());
|
||||
mgr.registerCommand(this, new CommandConfirmCode());
|
||||
mgr.registerCommand(this, new CommandDomain());
|
||||
mgr.registerCommand(this, new CommandClientBrand());
|
||||
EaglerAuthConfig authConf = conf.getAuthConfig();
|
||||
conf.setCracked(!BungeeCord.getInstance().getConfig().isOnlineMode() || !authConf.isEnableAuthentication());
|
||||
if(authConf.isEnableAuthentication() && authConf.isUseBuiltInAuthentication()) {
|
||||
@ -131,11 +146,11 @@ public class EaglerXBungee extends Plugin {
|
||||
mgr.registerCommand(this, new CommandEaglerPurge(authConf.getEaglerCommandName()));
|
||||
}
|
||||
}
|
||||
getProxy().registerChannel(SkinService.CHANNEL);
|
||||
getProxy().registerChannel(CapeServiceOffline.CHANNEL);
|
||||
getProxy().registerChannel(EaglerPipeline.UPDATE_CERT_CHANNEL);
|
||||
getProxy().registerChannel(VoiceService.CHANNEL);
|
||||
getProxy().registerChannel(EaglerPacketEventListener.FNAW_SKIN_ENABLE_CHANNEL);
|
||||
for(String str : GamePluginMessageProtocol.getAllChannels()) {
|
||||
getProxy().registerChannel(str);
|
||||
}
|
||||
getProxy().registerChannel(EaglerBackendRPCProtocol.CHANNEL_NAME);
|
||||
getProxy().registerChannel(EaglerBackendRPCProtocol.CHANNEL_NAME_READY);
|
||||
getProxy().registerChannel(EaglerPacketEventListener.GET_DOMAIN_CHANNEL);
|
||||
startListeners();
|
||||
if(closeInactiveConnections != null) {
|
||||
@ -206,6 +221,23 @@ public class EaglerXBungee extends Plugin {
|
||||
}else {
|
||||
logger().info("Voice chat disabled, add \"allow_voice: true\" to your listeners to enable");
|
||||
}
|
||||
if(updateServiceTasks != null) {
|
||||
updateServiceTasks.cancel();
|
||||
updateServiceTasks = null;
|
||||
}
|
||||
if(!conf.getUpdateConfig().isBlockAllClientUpdates()) {
|
||||
updateServiceTasks = new Timer("EaglerXBungee: Update Service Tasks");
|
||||
updateServiceTasks.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
EaglerUpdateSvc.updateTick();
|
||||
}catch(Throwable t) {
|
||||
logger().log(Level.SEVERE, "Error ticking update service!", t);
|
||||
}
|
||||
}
|
||||
}, 0l, 5000l);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -213,11 +245,12 @@ public class EaglerXBungee extends Plugin {
|
||||
PluginManager mgr = getProxy().getPluginManager();
|
||||
mgr.unregisterListeners(this);
|
||||
mgr.unregisterCommands(this);
|
||||
getProxy().unregisterChannel(SkinService.CHANNEL);
|
||||
getProxy().unregisterChannel(CapeServiceOffline.CHANNEL);
|
||||
getProxy().unregisterChannel(EaglerPipeline.UPDATE_CERT_CHANNEL);
|
||||
getProxy().unregisterChannel(VoiceService.CHANNEL);
|
||||
getProxy().unregisterChannel(EaglerPacketEventListener.FNAW_SKIN_ENABLE_CHANNEL);
|
||||
for(String str : GamePluginMessageProtocol.getAllChannels()) {
|
||||
getProxy().unregisterChannel(str);
|
||||
}
|
||||
getProxy().unregisterChannel(EaglerBackendRPCProtocol.CHANNEL_NAME);
|
||||
getProxy().unregisterChannel(EaglerBackendRPCProtocol.CHANNEL_NAME_READY);
|
||||
getProxy().unregisterChannel(EaglerPacketEventListener.GET_DOMAIN_CHANNEL);
|
||||
stopListeners();
|
||||
if(closeInactiveConnections != null) {
|
||||
closeInactiveConnections.cancel();
|
||||
@ -227,6 +260,10 @@ public class EaglerXBungee extends Plugin {
|
||||
skinServiceTasks.cancel();
|
||||
skinServiceTasks = null;
|
||||
}
|
||||
if(updateServiceTasks != null) {
|
||||
updateServiceTasks.cancel();
|
||||
updateServiceTasks = null;
|
||||
}
|
||||
skinService.shutdown();
|
||||
skinService = null;
|
||||
capeService.shutdown();
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,22 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public enum EnumVoiceState {
|
||||
SERVER_DISABLE,
|
||||
DISABLED,
|
||||
ENABLED;
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public enum EnumWebViewState {
|
||||
NOT_SUPPORTED,
|
||||
SERVER_DISABLE,
|
||||
CHANNEL_CLOSED,
|
||||
CHANNEL_OPEN;
|
||||
}
|
@ -0,0 +1,362 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeShowV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeShowV4EAG.EnumBadgePriority;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class NotificationBadgeBuilder {
|
||||
|
||||
public static enum BadgePriority {
|
||||
LOW, NORMAL, HIGHER, HIGHEST;
|
||||
}
|
||||
|
||||
private UUID badgeUUID = null;
|
||||
private BaseComponent bodyComponent = null;
|
||||
private BaseComponent titleComponent = null;
|
||||
private BaseComponent sourceComponent = null;
|
||||
private long originalTimestampSec = 0l;
|
||||
private boolean silent = false;
|
||||
private BadgePriority priority = BadgePriority.NORMAL;
|
||||
private UUID mainIconUUID = null;
|
||||
private UUID titleIconUUID = null;
|
||||
private int hideAfterSec = 10;
|
||||
private int expireAfterSec = 3600;
|
||||
private int backgroundColor = 0xFFFFFF;
|
||||
private int bodyTxtColor = 0xFFFFFF;
|
||||
private int titleTxtColor = 0xFFFFFF;
|
||||
private int sourceTxtColor = 0xFFFFFF;
|
||||
|
||||
private SPacketNotifBadgeShowV4EAG packetCache = null;
|
||||
private boolean packetDirty = true;
|
||||
|
||||
public NotificationBadgeBuilder() {
|
||||
originalTimestampSec = System.currentTimeMillis() / 1000l;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder(NotificationBadgeBuilder builder) {
|
||||
badgeUUID = builder.badgeUUID;
|
||||
bodyComponent = builder.bodyComponent;
|
||||
titleComponent = builder.titleComponent;
|
||||
sourceComponent = builder.sourceComponent;
|
||||
originalTimestampSec = builder.originalTimestampSec;
|
||||
silent = builder.silent;
|
||||
priority = builder.priority;
|
||||
mainIconUUID = builder.mainIconUUID;
|
||||
titleIconUUID = builder.titleIconUUID;
|
||||
hideAfterSec = builder.hideAfterSec;
|
||||
backgroundColor = builder.backgroundColor;
|
||||
bodyTxtColor = builder.bodyTxtColor;
|
||||
titleTxtColor = builder.titleTxtColor;
|
||||
sourceTxtColor = builder.sourceTxtColor;
|
||||
packetCache = !builder.packetDirty ? builder.packetCache : null;
|
||||
packetDirty = builder.packetDirty;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder(SPacketNotifBadgeShowV4EAG packet) {
|
||||
badgeUUID = new UUID(packet.badgeUUIDMost, packet.badgeUUIDLeast);
|
||||
try {
|
||||
bodyComponent = ComponentSerializer.deserialize(packet.bodyComponent);
|
||||
}catch(Throwable t) {
|
||||
bodyComponent = new TextComponent(packet.bodyComponent);
|
||||
}
|
||||
try {
|
||||
titleComponent = ComponentSerializer.deserialize(packet.titleComponent);
|
||||
}catch(Throwable t) {
|
||||
titleComponent = new TextComponent(packet.titleComponent);
|
||||
}
|
||||
try {
|
||||
sourceComponent = ComponentSerializer.deserialize(packet.sourceComponent);
|
||||
}catch(Throwable t) {
|
||||
sourceComponent = new TextComponent(packet.sourceComponent);
|
||||
}
|
||||
originalTimestampSec = packet.originalTimestampSec;
|
||||
silent = packet.silent;
|
||||
switch(packet.priority) {
|
||||
case LOW:
|
||||
default:
|
||||
priority = BadgePriority.LOW;
|
||||
break;
|
||||
case NORMAL:
|
||||
priority = BadgePriority.NORMAL;
|
||||
break;
|
||||
case HIGHER:
|
||||
priority = BadgePriority.HIGHER;
|
||||
break;
|
||||
case HIGHEST:
|
||||
priority = BadgePriority.HIGHEST;
|
||||
break;
|
||||
}
|
||||
mainIconUUID = new UUID(packet.mainIconUUIDMost, packet.mainIconUUIDLeast);
|
||||
titleIconUUID = new UUID(packet.titleIconUUIDMost, packet.titleIconUUIDLeast);
|
||||
hideAfterSec = packet.hideAfterSec;
|
||||
backgroundColor = packet.backgroundColor;
|
||||
bodyTxtColor = packet.bodyTxtColor;
|
||||
titleTxtColor = packet.titleTxtColor;
|
||||
sourceTxtColor = packet.sourceTxtColor;
|
||||
packetCache = packet;
|
||||
packetDirty = false;
|
||||
}
|
||||
|
||||
public UUID getBadgeUUID() {
|
||||
return badgeUUID;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setBadgeUUID(UUID badgeUUID) {
|
||||
this.badgeUUID = badgeUUID;
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setBadgeUUIDRandom() {
|
||||
this.badgeUUID = UUID.randomUUID();
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BaseComponent getBodyComponent() {
|
||||
return bodyComponent;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setBodyComponent(BaseComponent bodyComponent) {
|
||||
this.bodyComponent = bodyComponent;
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setBodyComponent(String bodyText) {
|
||||
this.bodyComponent = new TextComponent(bodyText);
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BaseComponent getTitleComponent() {
|
||||
return titleComponent;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setTitleComponent(BaseComponent titleComponent) {
|
||||
this.titleComponent = titleComponent;
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setTitleComponent(String titleText) {
|
||||
this.titleComponent = new TextComponent(titleText);
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BaseComponent getSourceComponent() {
|
||||
return sourceComponent;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setSourceComponent(BaseComponent sourceComponent) {
|
||||
this.sourceComponent = sourceComponent;
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setSourceComponent(String sourceText) {
|
||||
this.sourceComponent = new TextComponent(sourceText);
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getOriginalTimestampSec() {
|
||||
return originalTimestampSec;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setOriginalTimestampSec(long originalTimestampSec) {
|
||||
this.originalTimestampSec = originalTimestampSec;
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isSilent() {
|
||||
return silent;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setSilent(boolean silent) {
|
||||
this.silent = silent;
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BadgePriority getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setPriority(BadgePriority priority) {
|
||||
this.priority = priority;
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UUID getMainIconUUID() {
|
||||
return mainIconUUID;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setMainIconUUID(UUID mainIconUUID) {
|
||||
this.mainIconUUID = mainIconUUID;
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UUID getTitleIconUUID() {
|
||||
return titleIconUUID;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setTitleIconUUID(UUID titleIconUUID) {
|
||||
this.titleIconUUID = titleIconUUID;
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getHideAfterSec() {
|
||||
return hideAfterSec;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setHideAfterSec(int hideAfterSec) {
|
||||
this.hideAfterSec = hideAfterSec;
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getExpireAfterSec() {
|
||||
return expireAfterSec;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setExpireAfterSec(int expireAfterSec) {
|
||||
this.expireAfterSec = expireAfterSec;
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getBackgroundColor() {
|
||||
return backgroundColor;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setBackgroundColor(int backgroundColor) {
|
||||
this.backgroundColor = backgroundColor;
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getBodyTxtColorRGB() {
|
||||
return bodyTxtColor;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setBodyTxtColorRGB(int colorRGB) {
|
||||
this.bodyTxtColor = colorRGB;
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setBodyTxtColorRGB(int colorR, int colorG, int colorB) {
|
||||
this.bodyTxtColor = (colorR << 16) | (colorG << 8) | colorB;
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getTitleTxtColorRGB() {
|
||||
return titleTxtColor;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setTitleTxtColorRGB(int colorRGB) {
|
||||
this.titleTxtColor = colorRGB;
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setTitleTxtColorRGB(int colorR, int colorG, int colorB) {
|
||||
this.titleTxtColor = (colorR << 16) | (colorG << 8) | colorB;
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getSourceTxtColorRGB() {
|
||||
return sourceTxtColor;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setSourceTxtColorRGB(int colorRGB) {
|
||||
this.sourceTxtColor = colorRGB;
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotificationBadgeBuilder setSourceTxtColorRGB(int colorR, int colorG, int colorB) {
|
||||
this.sourceTxtColor = (colorR << 16) | (colorG << 8) | colorB;
|
||||
this.packetDirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Object clone() {
|
||||
return new NotificationBadgeBuilder(this);
|
||||
}
|
||||
|
||||
public SPacketNotifBadgeShowV4EAG buildPacket() {
|
||||
if(packetDirty || packetCache == null) {
|
||||
if(badgeUUID == null) {
|
||||
badgeUUID = UUID.randomUUID();
|
||||
}else if(badgeUUID.getMostSignificantBits() == 0l && badgeUUID.getLeastSignificantBits() == 0l) {
|
||||
throw new IllegalStateException("Badge UUID cannot be 0!");
|
||||
}
|
||||
EnumBadgePriority internalPriority;
|
||||
switch(priority) {
|
||||
case LOW:
|
||||
default:
|
||||
internalPriority = EnumBadgePriority.LOW;
|
||||
break;
|
||||
case NORMAL:
|
||||
internalPriority = EnumBadgePriority.NORMAL;
|
||||
break;
|
||||
case HIGHER:
|
||||
internalPriority = EnumBadgePriority.HIGHER;
|
||||
break;
|
||||
case HIGHEST:
|
||||
internalPriority = EnumBadgePriority.HIGHEST;
|
||||
break;
|
||||
}
|
||||
String bodyComp = bodyComponent != null ? ComponentSerializer.toString(bodyComponent) : "";
|
||||
if(bodyComp.length() > 32767) {
|
||||
throw new IllegalStateException("Body component is longer than 32767 chars serialized!");
|
||||
}
|
||||
String titleComp = titleComponent != null ? ComponentSerializer.toString(titleComponent) : "";
|
||||
if(titleComp.length() > 255) {
|
||||
throw new IllegalStateException("Title component is longer than 255 chars serialized!");
|
||||
}
|
||||
String sourceComp = sourceComponent != null ? ComponentSerializer.toString(sourceComponent) : "";
|
||||
if(sourceComp.length() > 255) {
|
||||
throw new IllegalStateException("Body component is longer than 255 chars serialized!");
|
||||
}
|
||||
packetCache = new SPacketNotifBadgeShowV4EAG(badgeUUID.getMostSignificantBits(),
|
||||
badgeUUID.getLeastSignificantBits(), bodyComp, titleComp, sourceComp, originalTimestampSec, silent,
|
||||
internalPriority, mainIconUUID != null ? mainIconUUID.getMostSignificantBits() : 0l,
|
||||
mainIconUUID != null ? mainIconUUID.getLeastSignificantBits() : 0l,
|
||||
titleIconUUID != null ? titleIconUUID.getMostSignificantBits() : 0l,
|
||||
titleIconUUID != null ? titleIconUUID.getLeastSignificantBits() : 0l, hideAfterSec, expireAfterSec,
|
||||
backgroundColor, bodyTxtColor, titleTxtColor, sourceTxtColor);
|
||||
packetDirty = false;
|
||||
}
|
||||
return packetCache;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.plugin.Cancellable;
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class EaglercraftClientBrandEvent extends Event implements Cancellable {
|
||||
|
||||
private final String clientBrand;
|
||||
private final String clientVersion;
|
||||
private final String origin;
|
||||
private final int protocolVersion;
|
||||
private final InetAddress remoteAddress;
|
||||
private boolean cancelled;
|
||||
private BaseComponent message;
|
||||
|
||||
public EaglercraftClientBrandEvent(String clientBrand, String clientVersion, String origin, int protocolVersion,
|
||||
InetAddress remoteAddress) {
|
||||
this.clientBrand = clientBrand;
|
||||
this.clientVersion = clientVersion;
|
||||
this.origin = origin;
|
||||
this.protocolVersion = protocolVersion;
|
||||
this.remoteAddress = remoteAddress;
|
||||
}
|
||||
|
||||
public BaseComponent getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(BaseComponent message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getClientBrand() {
|
||||
return clientBrand;
|
||||
}
|
||||
|
||||
public String getClientVersion() {
|
||||
return clientVersion;
|
||||
}
|
||||
|
||||
public String getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
public int getProtocolVersion() {
|
||||
return protocolVersion;
|
||||
}
|
||||
|
||||
public InetAddress getRemoteAddress() {
|
||||
return remoteAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
public void setKickMessage(String message) {
|
||||
this.cancelled = true;
|
||||
this.message = new TextComponent(message);
|
||||
}
|
||||
|
||||
public void setKickMessage(BaseComponent message) {
|
||||
this.cancelled = true;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,206 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2023 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class EaglercraftHandleAuthCookieEvent extends Event {
|
||||
|
||||
public static enum AuthResponse {
|
||||
ALLOW, DENY, REQUIRE_AUTH
|
||||
}
|
||||
|
||||
private final EaglerListenerConfig listener;
|
||||
private final InetAddress authRemoteAddress;
|
||||
private final String authOrigin;
|
||||
private final boolean enableCookies;
|
||||
private final byte[] cookieData;
|
||||
private final EaglercraftIsAuthRequiredEvent.AuthMethod eventAuthMethod;
|
||||
private final String eventAuthMessage;
|
||||
private final Object authAttachment;
|
||||
|
||||
private AuthResponse eventResponse;
|
||||
private byte[] authUsername;
|
||||
private String authProfileUsername;
|
||||
private UUID authProfileUUID;
|
||||
private String authRequestedServerRespose;
|
||||
private String authDeniedMessage = "Bad Cookie!";
|
||||
private String applyTexturesPropValue;
|
||||
private String applyTexturesPropSignature;
|
||||
private boolean overrideEaglerToVanillaSkins;
|
||||
private Consumer<EaglercraftHandleAuthCookieEvent> continueThread;
|
||||
private Runnable continueRunnable;
|
||||
private volatile boolean hasContinue = false;
|
||||
|
||||
public EaglercraftHandleAuthCookieEvent(EaglerListenerConfig listener, InetAddress authRemoteAddress,
|
||||
String authOrigin, byte[] authUsername, String authProfileUsername, UUID authProfileUUID,
|
||||
boolean enableCookies, byte[] cookieData, EaglercraftIsAuthRequiredEvent.AuthMethod eventAuthMethod,
|
||||
String eventAuthMessage, Object authAttachment, String authRequestedServerRespose,
|
||||
Consumer<EaglercraftHandleAuthCookieEvent> continueThread) {
|
||||
this.listener = listener;
|
||||
this.authRemoteAddress = authRemoteAddress;
|
||||
this.authOrigin = authOrigin;
|
||||
this.authUsername = authUsername;
|
||||
this.authProfileUsername = authProfileUsername;
|
||||
this.authProfileUUID = authProfileUUID;
|
||||
this.enableCookies = enableCookies;
|
||||
this.cookieData = cookieData;
|
||||
this.eventAuthMethod = eventAuthMethod;
|
||||
this.eventAuthMessage = eventAuthMessage;
|
||||
this.authAttachment = authAttachment;
|
||||
this.authRequestedServerRespose = authRequestedServerRespose;
|
||||
this.continueThread = continueThread;
|
||||
}
|
||||
|
||||
public EaglerListenerConfig getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
public InetAddress getRemoteAddress() {
|
||||
return authRemoteAddress;
|
||||
}
|
||||
|
||||
public String getOriginHeader() {
|
||||
return authOrigin;
|
||||
}
|
||||
|
||||
public boolean getCookiesEnabled() {
|
||||
return enableCookies;
|
||||
}
|
||||
|
||||
public byte[] getCookieData() {
|
||||
return cookieData;
|
||||
}
|
||||
|
||||
public String getCookieDataString() {
|
||||
return cookieData != null ? new String(cookieData, StandardCharsets.UTF_8) : null;
|
||||
}
|
||||
|
||||
public byte[] getAuthUsername() {
|
||||
return authUsername;
|
||||
}
|
||||
|
||||
public String getProfileUsername() {
|
||||
return authProfileUsername;
|
||||
}
|
||||
|
||||
public void setProfileUsername(String username) {
|
||||
this.authProfileUsername = username;
|
||||
}
|
||||
|
||||
public UUID getProfileUUID() {
|
||||
return authProfileUUID;
|
||||
}
|
||||
|
||||
public void setProfileUUID(UUID uuid) {
|
||||
this.authProfileUUID = uuid;
|
||||
}
|
||||
|
||||
public EaglercraftIsAuthRequiredEvent.AuthMethod getAuthType() {
|
||||
return eventAuthMethod;
|
||||
}
|
||||
|
||||
public String getAuthMessage() {
|
||||
return eventAuthMessage;
|
||||
}
|
||||
|
||||
public <T> T getAuthAttachment() {
|
||||
return (T)authAttachment;
|
||||
}
|
||||
|
||||
public String getAuthRequestedServer() {
|
||||
return authRequestedServerRespose;
|
||||
}
|
||||
|
||||
public void setAuthRequestedServer(String server) {
|
||||
this.authRequestedServerRespose = server;
|
||||
}
|
||||
|
||||
public void setLoginAllowed() {
|
||||
this.eventResponse = AuthResponse.ALLOW;
|
||||
this.authDeniedMessage = null;
|
||||
}
|
||||
|
||||
public void setLoginPasswordRequired() {
|
||||
this.eventResponse = AuthResponse.REQUIRE_AUTH;
|
||||
this.authDeniedMessage = null;
|
||||
}
|
||||
|
||||
public void setLoginDenied(String message) {
|
||||
this.eventResponse = AuthResponse.DENY;
|
||||
this.authDeniedMessage = message;
|
||||
}
|
||||
|
||||
public AuthResponse getLoginAllowed() {
|
||||
return eventResponse;
|
||||
}
|
||||
|
||||
public String getLoginDeniedMessage() {
|
||||
return authDeniedMessage;
|
||||
}
|
||||
|
||||
public Runnable makeAsyncContinue() {
|
||||
if(continueRunnable == null) {
|
||||
continueRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if(!hasContinue) {
|
||||
hasContinue = true;
|
||||
continueThread.accept(EaglercraftHandleAuthCookieEvent.this);
|
||||
}else {
|
||||
throw new IllegalStateException("Thread was already continued from a different function! Auth plugin conflict?");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return continueRunnable;
|
||||
}
|
||||
|
||||
public boolean isAsyncContinue() {
|
||||
return continueRunnable != null;
|
||||
}
|
||||
|
||||
public void doDirectContinue() {
|
||||
continueThread.accept(this);
|
||||
}
|
||||
|
||||
public void applyTexturesProperty(String value, String signature) {
|
||||
applyTexturesPropValue = value;
|
||||
applyTexturesPropSignature = signature;
|
||||
}
|
||||
|
||||
public String getApplyTexturesPropertyValue() {
|
||||
return applyTexturesPropValue;
|
||||
}
|
||||
|
||||
public String getApplyTexturesPropertySignature() {
|
||||
return applyTexturesPropSignature;
|
||||
}
|
||||
|
||||
public void setOverrideEaglerToVanillaSkins(boolean overrideEaglerToVanillaSkins) {
|
||||
this.overrideEaglerToVanillaSkins = overrideEaglerToVanillaSkins;
|
||||
}
|
||||
|
||||
public boolean isOverrideEaglerToVanillaSkins() {
|
||||
return overrideEaglerToVanillaSkins;
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@ -29,17 +30,19 @@ public class EaglercraftHandleAuthPasswordEvent extends Event {
|
||||
}
|
||||
|
||||
private final EaglerListenerConfig listener;
|
||||
private final InetAddress authRemoteAddress;
|
||||
private final String authOrigin;
|
||||
private final InetAddress authRemoteAddress;
|
||||
private final String authOrigin;
|
||||
private final byte[] authUsername;
|
||||
private final byte[] authSaltingData;
|
||||
private final byte[] authPasswordData;
|
||||
private final boolean enableCookies;
|
||||
private final byte[] cookieData;
|
||||
private final EaglercraftIsAuthRequiredEvent.AuthMethod eventAuthMethod;
|
||||
private final String eventAuthMessage;
|
||||
private final Object authAttachment;
|
||||
|
||||
private AuthResponse eventResponse;
|
||||
private CharSequence authProfileUsername;
|
||||
private String authProfileUsername;
|
||||
private UUID authProfileUUID;
|
||||
private String authRequestedServerRespose;
|
||||
private String authDeniedMessage = "Password Incorrect!";
|
||||
@ -51,10 +54,10 @@ public class EaglercraftHandleAuthPasswordEvent extends Event {
|
||||
private volatile boolean hasContinue = false;
|
||||
|
||||
public EaglercraftHandleAuthPasswordEvent(EaglerListenerConfig listener, InetAddress authRemoteAddress,
|
||||
String authOrigin, byte[] authUsername, byte[] authSaltingData, CharSequence authProfileUsername,
|
||||
UUID authProfileUUID, byte[] authPasswordData, EaglercraftIsAuthRequiredEvent.AuthMethod eventAuthMethod,
|
||||
String eventAuthMessage, Object authAttachment, String authRequestedServerRespose,
|
||||
Consumer<EaglercraftHandleAuthPasswordEvent> continueThread) {
|
||||
String authOrigin, byte[] authUsername, byte[] authSaltingData, String authProfileUsername,
|
||||
UUID authProfileUUID, byte[] authPasswordData, boolean enableCookies, byte[] cookieData,
|
||||
EaglercraftIsAuthRequiredEvent.AuthMethod eventAuthMethod, String eventAuthMessage, Object authAttachment,
|
||||
String authRequestedServerRespose, Consumer<EaglercraftHandleAuthPasswordEvent> continueThread) {
|
||||
this.listener = listener;
|
||||
this.authRemoteAddress = authRemoteAddress;
|
||||
this.authOrigin = authOrigin;
|
||||
@ -63,6 +66,8 @@ public class EaglercraftHandleAuthPasswordEvent extends Event {
|
||||
this.authProfileUsername = authProfileUsername;
|
||||
this.authProfileUUID = authProfileUUID;
|
||||
this.authPasswordData = authPasswordData;
|
||||
this.enableCookies = enableCookies;
|
||||
this.cookieData = cookieData;
|
||||
this.eventAuthMethod = eventAuthMethod;
|
||||
this.eventAuthMessage = eventAuthMessage;
|
||||
this.authAttachment = authAttachment;
|
||||
@ -90,11 +95,23 @@ public class EaglercraftHandleAuthPasswordEvent extends Event {
|
||||
return authSaltingData;
|
||||
}
|
||||
|
||||
public CharSequence getProfileUsername() {
|
||||
public boolean getCookiesEnabled() {
|
||||
return enableCookies;
|
||||
}
|
||||
|
||||
public byte[] getCookieData() {
|
||||
return cookieData;
|
||||
}
|
||||
|
||||
public String getCookieDataString() {
|
||||
return cookieData != null ? new String(cookieData, StandardCharsets.UTF_8) : null;
|
||||
}
|
||||
|
||||
public String getProfileUsername() {
|
||||
return authProfileUsername;
|
||||
}
|
||||
|
||||
public void setProfileUsername(CharSequence username) {
|
||||
public void setProfileUsername(String username) {
|
||||
this.authProfileUsername = username;
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerList
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2023 lax1dude. All Rights Reserved.
|
||||
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
@ -43,7 +43,7 @@ public class EaglercraftIsAuthRequiredEvent extends Event {
|
||||
}
|
||||
|
||||
private final EaglerListenerConfig listener;
|
||||
private AuthResponse authResponse;
|
||||
private AuthResponse authResponse;
|
||||
private final InetAddress authRemoteAddress;
|
||||
private final String authOrigin;
|
||||
private final boolean wantsAuth;
|
||||
@ -53,6 +53,7 @@ public class EaglercraftIsAuthRequiredEvent extends Event {
|
||||
private String eventAuthMessage = "enter the code:";
|
||||
private String kickUserMessage = "Login Denied";
|
||||
private Object authAttachment;
|
||||
private boolean enableCookieAuth;
|
||||
private Consumer<EaglercraftIsAuthRequiredEvent> continueThread;
|
||||
private Runnable continueRunnable;
|
||||
private volatile boolean hasContinue = false;
|
||||
@ -117,6 +118,14 @@ public class EaglercraftIsAuthRequiredEvent extends Event {
|
||||
this.authAttachment = authAttachment;
|
||||
}
|
||||
|
||||
public boolean getEnableCookieAuth() {
|
||||
return enableCookieAuth;
|
||||
}
|
||||
|
||||
public void setEnableCookieAuth(boolean enable) {
|
||||
this.enableCookieAuth = enable;
|
||||
}
|
||||
|
||||
public boolean shouldKickUser() {
|
||||
return authResponse == null || authResponse == AuthResponse.DENY;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
|
||||
/**
|
||||
@ -21,13 +22,15 @@ import net.md_5.bungee.api.plugin.Event;
|
||||
*/
|
||||
public class EaglercraftRegisterCapeEvent extends Event {
|
||||
|
||||
private final Object authAttachment;
|
||||
private final String username;
|
||||
private final UUID uuid;
|
||||
private byte[] customTex = null;
|
||||
|
||||
public EaglercraftRegisterCapeEvent(String username, UUID uuid) {
|
||||
public EaglercraftRegisterCapeEvent(String username, UUID uuid, Object authAttachment) {
|
||||
this.username = username;
|
||||
this.uuid = uuid;
|
||||
this.authAttachment = authAttachment;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
@ -47,10 +50,13 @@ public class EaglercraftRegisterCapeEvent extends Event {
|
||||
customTex[4] = (byte)(p & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tex raw 32x32 pixel RGBA texture (4096 bytes long), see capes in "sources/resources/assets/eagler/capes"
|
||||
*/
|
||||
public void setForceUseCustom(byte[] tex) {
|
||||
customTex = new byte[1 + tex.length];
|
||||
customTex = new byte[1174];
|
||||
customTex[0] = (byte)2;
|
||||
System.arraycopy(tex, 0, customTex, 1, tex.length);
|
||||
EaglerXBungeeAPIHelper.convertCape32x32RGBAto23x17RGB(tex, 0, customTex, 1);
|
||||
}
|
||||
|
||||
public void setForceUseCustomByPacket(byte[] packet) {
|
||||
@ -60,4 +66,8 @@ public class EaglercraftRegisterCapeEvent extends Event {
|
||||
public byte[] getForceSetUseCustomPacket() {
|
||||
return customTex;
|
||||
}
|
||||
|
||||
public <T> T getAuthAttachment() {
|
||||
return (T)authAttachment;
|
||||
}
|
||||
}
|
||||
|
@ -22,30 +22,29 @@ import net.md_5.bungee.protocol.Property;
|
||||
*/
|
||||
public class EaglercraftRegisterSkinEvent extends Event {
|
||||
|
||||
private final Object authAttachment;
|
||||
private final String username;
|
||||
private final UUID uuid;
|
||||
private Property useMojangProfileProperty = null;
|
||||
private boolean useLoginResultTextures = false;
|
||||
private byte[] customTex = null;
|
||||
private String customURL = null;
|
||||
|
||||
public EaglercraftRegisterSkinEvent(String username, UUID uuid) {
|
||||
public EaglercraftRegisterSkinEvent(String username, UUID uuid, Object authAttachment) {
|
||||
this.username = username;
|
||||
this.uuid = uuid;
|
||||
this.authAttachment = authAttachment;
|
||||
}
|
||||
|
||||
public void setForceUseMojangProfileProperty(Property prop) {
|
||||
useMojangProfileProperty = prop;
|
||||
useLoginResultTextures = false;
|
||||
customTex = null;
|
||||
customURL = null;
|
||||
}
|
||||
|
||||
public void setForceUseLoginResultObjectTextures(boolean b) {
|
||||
useMojangProfileProperty = null;
|
||||
useLoginResultTextures = b;
|
||||
customTex = null;
|
||||
customURL = null;
|
||||
}
|
||||
|
||||
public void setForceUsePreset(int p) {
|
||||
@ -57,9 +56,11 @@ public class EaglercraftRegisterSkinEvent extends Event {
|
||||
customTex[2] = (byte)(p >>> 16);
|
||||
customTex[3] = (byte)(p >>> 8);
|
||||
customTex[4] = (byte)(p & 0xFF);
|
||||
customURL = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tex raw 64x64 pixel RGBA texture (16384 bytes long)
|
||||
*/
|
||||
public void setForceUseCustom(int model, byte[] tex) {
|
||||
useMojangProfileProperty = null;
|
||||
useLoginResultTextures = false;
|
||||
@ -67,21 +68,12 @@ public class EaglercraftRegisterSkinEvent extends Event {
|
||||
customTex[0] = (byte)2;
|
||||
customTex[1] = (byte)model;
|
||||
System.arraycopy(tex, 0, customTex, 2, tex.length);
|
||||
customURL = null;
|
||||
}
|
||||
|
||||
public void setForceUseCustomByPacket(byte[] packet) {
|
||||
useMojangProfileProperty = null;
|
||||
useLoginResultTextures = false;
|
||||
customTex = packet;
|
||||
customURL = null;
|
||||
}
|
||||
|
||||
public void setForceUseURL(String url) {
|
||||
useMojangProfileProperty = null;
|
||||
useLoginResultTextures = false;
|
||||
customTex = null;
|
||||
customURL = url;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
@ -104,8 +96,8 @@ public class EaglercraftRegisterSkinEvent extends Event {
|
||||
return customTex;
|
||||
}
|
||||
|
||||
public String getForceSetUseURL() {
|
||||
return customURL;
|
||||
public <T> T getAuthAttachment() {
|
||||
return (T)authAttachment;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,91 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.query.RevokeSessionQueryHandler;
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class EaglercraftRevokeSessionQueryEvent extends Event {
|
||||
|
||||
private final InetAddress remoteAddress;
|
||||
private final String origin;
|
||||
private final byte[] cookieData;
|
||||
private final RevokeSessionQueryHandler queryHandler;
|
||||
private EnumSessionRevokeStatus revokeStatus;
|
||||
private boolean shouldDeleteCookie;
|
||||
|
||||
public static enum EnumSessionRevokeStatus {
|
||||
SUCCESS("ok", -1), FAILED_NOT_SUPPORTED("error", 1), FAILED_NOT_ALLOWED("error", 2),
|
||||
FAILED_NOT_FOUND("error", 3), FAILED_SERVER_ERROR("error", 4);
|
||||
|
||||
public final String status;
|
||||
public final int code;
|
||||
|
||||
private EnumSessionRevokeStatus(String status, int code) {
|
||||
this.status = status;
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
public EaglercraftRevokeSessionQueryEvent(InetAddress remoteAddress, String origin, byte[] cookieData,
|
||||
RevokeSessionQueryHandler queryHandler) {
|
||||
this.remoteAddress = remoteAddress;
|
||||
this.origin = origin;
|
||||
this.cookieData = cookieData;
|
||||
this.queryHandler = queryHandler;
|
||||
this.revokeStatus = EnumSessionRevokeStatus.FAILED_NOT_SUPPORTED;
|
||||
this.shouldDeleteCookie = false;
|
||||
}
|
||||
|
||||
public InetAddress getRemoteAddress() {
|
||||
return remoteAddress;
|
||||
}
|
||||
|
||||
public String getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
public byte[] getCookieData() {
|
||||
return cookieData;
|
||||
}
|
||||
|
||||
public String getCookieDataString() {
|
||||
return new String(cookieData, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public RevokeSessionQueryHandler getQuery() {
|
||||
return queryHandler;
|
||||
}
|
||||
|
||||
public void setResultStatus(EnumSessionRevokeStatus revokeStatus) {
|
||||
this.revokeStatus = revokeStatus;
|
||||
}
|
||||
|
||||
public EnumSessionRevokeStatus getResultStatus() {
|
||||
return revokeStatus;
|
||||
}
|
||||
|
||||
public boolean getShouldDeleteCookie() {
|
||||
return shouldDeleteCookie;
|
||||
}
|
||||
|
||||
public void setShouldDeleteCookie(boolean b) {
|
||||
this.shouldDeleteCookie = b;
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class EaglercraftVoiceStatusChangeEvent extends Event {
|
||||
|
||||
public static enum EnumVoiceState {
|
||||
SERVER_DISABLE, DISABLED, ENABLED;
|
||||
}
|
||||
|
||||
private final ProxiedPlayer playerObj;
|
||||
private final EaglerListenerConfig listener;
|
||||
private final EaglerInitialHandler eaglerHandler;
|
||||
private final EnumVoiceState voiceStateOld;
|
||||
private final EnumVoiceState voiceStateNew;
|
||||
|
||||
public EaglercraftVoiceStatusChangeEvent(ProxiedPlayer playerObj, EaglerListenerConfig listener,
|
||||
EaglerInitialHandler eaglerHandler, EnumVoiceState voiceStateOld, EnumVoiceState voiceStateNew) {
|
||||
this.playerObj = playerObj;
|
||||
this.listener = listener;
|
||||
this.eaglerHandler = eaglerHandler;
|
||||
this.voiceStateOld = voiceStateOld;
|
||||
this.voiceStateNew = voiceStateNew;
|
||||
}
|
||||
|
||||
public ProxiedPlayer getPlayerObj() {
|
||||
return playerObj;
|
||||
}
|
||||
|
||||
public EaglerListenerConfig getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
public EaglerInitialHandler getEaglerHandler() {
|
||||
return eaglerHandler;
|
||||
}
|
||||
|
||||
public EnumVoiceState getVoiceStateOld() {
|
||||
return voiceStateOld;
|
||||
}
|
||||
|
||||
public EnumVoiceState getVoiceStateNew() {
|
||||
return voiceStateNew;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
|
||||
import net.md_5.bungee.api.plugin.Cancellable;
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class EaglercraftWebSocketOpenEvent extends Event implements Cancellable {
|
||||
|
||||
private final Channel channel;
|
||||
private final EaglerListenerConfig listener;
|
||||
private final String realIP;
|
||||
private final String origin;
|
||||
private final String userAgent;
|
||||
private boolean cancelled = false;
|
||||
|
||||
public EaglercraftWebSocketOpenEvent(Channel channel, EaglerListenerConfig listener, String realIP, String origin, String userAgent) {
|
||||
this.channel = channel;
|
||||
this.listener = listener;
|
||||
this.realIP = realIP;
|
||||
this.origin = origin;
|
||||
this.userAgent = userAgent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean var1) {
|
||||
cancelled = var1;
|
||||
}
|
||||
|
||||
public Channel getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public EaglerListenerConfig getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
public String getRealIP() {
|
||||
return realIP;
|
||||
}
|
||||
|
||||
public String getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
public String getUserAgent() {
|
||||
return userAgent;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class EaglercraftWebViewChannelEvent extends Event {
|
||||
|
||||
public static enum EventType {
|
||||
CHANNEL_OPEN, CHANNEL_CLOSE;
|
||||
}
|
||||
|
||||
private final ProxiedPlayer player;
|
||||
private final EaglerListenerConfig listener;
|
||||
private final String channel;
|
||||
private final EventType type;
|
||||
|
||||
public EaglercraftWebViewChannelEvent(ProxiedPlayer player, EaglerListenerConfig listener, String channel, EventType type) {
|
||||
this.player = player;
|
||||
this.listener = listener;
|
||||
this.channel = channel;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public ProxiedPlayer getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public EaglerListenerConfig getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
public String getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public EventType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketWebViewMessageV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketWebViewMessageV4EAG;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class EaglercraftWebViewMessageEvent extends Event {
|
||||
|
||||
public static enum MessageType {
|
||||
STRING(SPacketWebViewMessageV4EAG.TYPE_STRING), BINARY(SPacketWebViewMessageV4EAG.TYPE_BINARY);
|
||||
|
||||
private final int id;
|
||||
|
||||
private MessageType(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
private static MessageType fromId(int id) {
|
||||
switch(id) {
|
||||
case CPacketWebViewMessageV4EAG.TYPE_STRING:
|
||||
return STRING;
|
||||
default:
|
||||
case CPacketWebViewMessageV4EAG.TYPE_BINARY:
|
||||
return BINARY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final ProxiedPlayer player;
|
||||
private final EaglerListenerConfig listener;
|
||||
private final String currentChannel;
|
||||
private final EaglerInitialHandler eaglerHandler;
|
||||
private final MessageType type;
|
||||
private final byte[] data;
|
||||
private String asString;
|
||||
|
||||
public EaglercraftWebViewMessageEvent(ProxiedPlayer player, EaglerListenerConfig listener, String currentChannel, MessageType type, byte[] data) {
|
||||
this.player = player;
|
||||
this.listener = listener;
|
||||
this.currentChannel = currentChannel;
|
||||
this.eaglerHandler = EaglerXBungeeAPIHelper.getEaglerHandle(player);
|
||||
this.type = type;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public EaglercraftWebViewMessageEvent(ProxiedPlayer player, EaglerListenerConfig listener, String currentChannel, CPacketWebViewMessageV4EAG packet) {
|
||||
this.player = player;
|
||||
this.listener = listener;
|
||||
this.currentChannel = currentChannel;
|
||||
this.eaglerHandler = EaglerXBungeeAPIHelper.getEaglerHandle(player);
|
||||
this.type = MessageType.fromId(packet.type);
|
||||
this.data = packet.data;
|
||||
}
|
||||
|
||||
public ProxiedPlayer getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public EaglerListenerConfig getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
public EaglerInitialHandler getEaglerHandle() {
|
||||
return eaglerHandler;
|
||||
}
|
||||
|
||||
public void sendResponse(MessageType type, byte[] data) {
|
||||
eaglerHandler.sendEaglerMessage(new SPacketWebViewMessageV4EAG(type.id, data));
|
||||
}
|
||||
|
||||
public void sendResponse(String str) {
|
||||
sendResponse(MessageType.STRING, str.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public void sendResponse(byte[] data) {
|
||||
sendResponse(MessageType.BINARY, data);
|
||||
}
|
||||
|
||||
public MessageType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public byte[] getAsBinary() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public String getAsString() {
|
||||
if(asString == null) {
|
||||
asString = new String(data, StandardCharsets.UTF_8);
|
||||
}
|
||||
return asString;
|
||||
}
|
||||
|
||||
public String getChannelName() {
|
||||
return currentChannel;
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.query;
|
||||
import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
|
||||
|
||||
/**
|
||||
@ -31,7 +32,7 @@ public interface MOTDConnection {
|
||||
long getConnectionTimestamp();
|
||||
|
||||
public default long getConnectionAge() {
|
||||
return System.currentTimeMillis() - getConnectionTimestamp();
|
||||
return EaglerXBungeeAPIHelper.steadyTimeMillis() - getConnectionTimestamp();
|
||||
}
|
||||
|
||||
void sendToUser();
|
||||
|
@ -5,6 +5,8 @@ import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2023 lax1dude. All Rights Reserved.
|
||||
*
|
||||
@ -28,7 +30,7 @@ public class AuthLoadingCache<K, V> {
|
||||
private V instance;
|
||||
|
||||
private CacheEntry(V instance) {
|
||||
this.lastHit = System.currentTimeMillis();
|
||||
this.lastHit = EaglerXBungeeAPIHelper.steadyTimeMillis();
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
@ -49,7 +51,7 @@ public class AuthLoadingCache<K, V> {
|
||||
private long cacheTimer;
|
||||
|
||||
public AuthLoadingCache(CacheLoader<K, V> provider, long cacheTTL) {
|
||||
this.cacheMap = new HashMap();
|
||||
this.cacheMap = new HashMap<>();
|
||||
this.provider = provider;
|
||||
this.cacheTTL = cacheTTL;
|
||||
}
|
||||
@ -66,7 +68,7 @@ public class AuthLoadingCache<K, V> {
|
||||
}
|
||||
return loaded;
|
||||
}else {
|
||||
etr.lastHit = System.currentTimeMillis();
|
||||
etr.lastHit = EaglerXBungeeAPIHelper.steadyTimeMillis();
|
||||
return etr.instance;
|
||||
}
|
||||
}
|
||||
@ -90,7 +92,7 @@ public class AuthLoadingCache<K, V> {
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
long millis = System.currentTimeMillis();
|
||||
long millis = EaglerXBungeeAPIHelper.steadyTimeMillis();
|
||||
if(millis - cacheTimer > (cacheTTL / 2L)) {
|
||||
cacheTimer = millis;
|
||||
synchronized(cacheMap) {
|
||||
|
@ -24,6 +24,7 @@ import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftHandleAuthPasswordEvent;
|
||||
@ -249,7 +250,7 @@ public class DefaultAuthSystem {
|
||||
this.checkRegistrationByName = databaseConnection.prepareStatement("SELECT Version, MojangUUID, MojangTextures, HashBase, HashSalt, Registered, RegisteredIP, LastLogin, LastLoginIP FROM eaglercraft_accounts WHERE MojangUsername = ?");
|
||||
this.setLastLogin = databaseConnection.prepareStatement("UPDATE eaglercraft_accounts SET LastLogin = ?, LastLoginIP = ? WHERE MojangUUID = ?");
|
||||
this.updateTextures = databaseConnection.prepareStatement("UPDATE eaglercraft_accounts SET MojangTextures = ? WHERE MojangUUID = ?");
|
||||
this.authLoadingCache = new AuthLoadingCache(new AccountLoader(), 120000l);
|
||||
this.authLoadingCache = new AuthLoadingCache<>(new AccountLoader(), 120000l);
|
||||
this.secureRandom = new SecureRandom();
|
||||
}
|
||||
|
||||
@ -473,7 +474,7 @@ public class DefaultAuthSystem {
|
||||
Property prop = props[i];
|
||||
if("textures".equals(prop.getName())) {
|
||||
byte[] texturesData = Base64.decodeBase64(prop.getValue());
|
||||
byte[] signatureData = prop.getSignature() == null ? new byte[0] : Base64.decodeBase64(prop.getSignature());
|
||||
byte[] signatureData = prop.getSignature() == null ? ArrayUtils.EMPTY_BYTE_ARRAY : Base64.decodeBase64(prop.getSignature());
|
||||
ByteArrayOutputStream bao = new ByteArrayOutputStream();
|
||||
DataOutputStream dao = new DataOutputStream(bao);
|
||||
dao.writeInt(texturesData.length);
|
||||
|
@ -0,0 +1,93 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.command;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
|
||||
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.chat.TextComponent;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Command;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.OverflowPacketException;
|
||||
import net.md_5.bungee.protocol.packet.PluginMessage;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class CommandClientBrand extends Command {
|
||||
|
||||
public CommandClientBrand() {
|
||||
super("client-brand", "eaglercraft.command.clientbrand", "clientbrand");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender var1, String[] var2) {
|
||||
if(var2.length == 1) {
|
||||
ProxiedPlayer player = BungeeCord.getInstance().getPlayer(var2[0]);
|
||||
if(player != null) {
|
||||
if(player.getPendingConnection() instanceof EaglerInitialHandler) {
|
||||
EaglerInitialHandler handler = (EaglerInitialHandler)player.getPendingConnection();
|
||||
var1.sendMessage(new TextComponent(ChatColor.BLUE + "Eagler Client Brand: " + ChatColor.WHITE + handler.getEaglerBrandString()));
|
||||
var1.sendMessage(new TextComponent(ChatColor.BLUE + "Eagler Client Version: " + ChatColor.WHITE + handler.getEaglerVersionString()));
|
||||
var1.sendMessage(new TextComponent(ChatColor.BLUE + "Eagler Client UUID: " + ChatColor.WHITE + handler.getClientBrandUUID()));
|
||||
var1.sendMessage(new TextComponent(ChatColor.BLUE + "Minecraft Client Brand: " + ChatColor.WHITE + decodeMCBrand(handler.getBrandMessage())));
|
||||
}else {
|
||||
var1.sendMessage(new TextComponent(ChatColor.RED + "That player is not using eaglercraft!"));
|
||||
}
|
||||
}else {
|
||||
var1.sendMessage(new TextComponent(ChatColor.RED + "That player was not found!"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(var2.length == 2) {
|
||||
ProxiedPlayer player = BungeeCord.getInstance().getPlayer(var2[1]);
|
||||
if(player != null) {
|
||||
if(player.getPendingConnection() instanceof EaglerInitialHandler) {
|
||||
EaglerInitialHandler handler = (EaglerInitialHandler)player.getPendingConnection();
|
||||
if("uuid".equalsIgnoreCase(var2[0])) {
|
||||
var1.sendMessage(new TextComponent(ChatColor.BLUE + "Eagler Client UUID: " + ChatColor.WHITE + handler.getClientBrandUUID()));
|
||||
return;
|
||||
}else if("name".equalsIgnoreCase(var2[0])) {
|
||||
var1.sendMessage(new TextComponent(ChatColor.BLUE + "Eagler Client Brand: " + ChatColor.WHITE + handler.getEaglerBrandString()));
|
||||
var1.sendMessage(new TextComponent(ChatColor.BLUE + "Eagler Client Version: " + ChatColor.WHITE + handler.getEaglerVersionString()));
|
||||
return;
|
||||
}else if("mc".equalsIgnoreCase(var2[0])) {
|
||||
var1.sendMessage(new TextComponent(ChatColor.BLUE + "Minecraft Client Brand: " + ChatColor.WHITE + decodeMCBrand(handler.getBrandMessage())));
|
||||
return;
|
||||
}
|
||||
}else {
|
||||
var1.sendMessage(new TextComponent(ChatColor.RED + "That player is not using eaglercraft!"));
|
||||
return;
|
||||
}
|
||||
}else {
|
||||
var1.sendMessage(new TextComponent(ChatColor.RED + "That player was not found!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
var1.sendMessage(new TextComponent(ChatColor.RED + "Usage: /client-brand [uuid|name|mc] <username>"));
|
||||
}
|
||||
|
||||
private static String decodeMCBrand(PluginMessage pkt) {
|
||||
if(pkt == null) {
|
||||
return "null";
|
||||
}
|
||||
try {
|
||||
return DefinedPacket.readString(Unpooled.wrappedBuffer(pkt.getData()), 64);
|
||||
}catch(OverflowPacketException | IndexOutOfBoundsException ex) {
|
||||
return "null";
|
||||
}
|
||||
}
|
||||
}
|
@ -45,7 +45,7 @@ public class CommandDomain extends Command {
|
||||
var1.sendMessage(new TextComponent(ChatColor.RED + "That user is not using Eaglercraft"));
|
||||
return;
|
||||
}
|
||||
String origin = ((EaglerInitialHandler)conn).origin;
|
||||
String origin = ((EaglerInitialHandler)conn).getOrigin();
|
||||
if(origin != null) {
|
||||
var1.sendMessage(new TextComponent(ChatColor.BLUE + "Domain of " + var2[0] + " is '" + origin + "'"));
|
||||
}else {
|
||||
|
@ -3,10 +3,12 @@ package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
@ -14,6 +16,7 @@ import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
@ -26,6 +29,7 @@ import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.web.HttpContentType;
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
import net.md_5.bungee.config.ConfigurationProvider;
|
||||
@ -50,7 +54,7 @@ import net.md_5.bungee.protocol.Property;
|
||||
public class EaglerBungeeConfig {
|
||||
|
||||
public static EaglerBungeeConfig loadConfig(File directory) throws IOException {
|
||||
Map<String, HttpContentType> contentTypes = new HashMap();
|
||||
Map<String, HttpContentType> contentTypes = new HashMap<>();
|
||||
|
||||
try(InputStream is = new FileInputStream(getConfigFile(directory, "http_mime_types.json"))) {
|
||||
loadMimeTypes(is, contentTypes);
|
||||
@ -68,6 +72,7 @@ public class EaglerBungeeConfig {
|
||||
|
||||
Configuration configYml = prov.load(getConfigFile(directory, "settings.yml"));
|
||||
String serverName = configYml.getString("server_name", "EaglercraftXBungee Server");
|
||||
EaglerXBungeeAPIHelper.getTemplateGlobals().put("server_name", serverName);
|
||||
String serverUUIDString = configYml.getString("server_uuid", null);
|
||||
if(serverUUIDString == null) {
|
||||
throw new IOException("You must specify a server_uuid!");
|
||||
@ -85,7 +90,7 @@ public class EaglerBungeeConfig {
|
||||
|
||||
Configuration listenerYml = prov.load(getConfigFile(directory, "listeners.yml"));
|
||||
Iterator<String> listeners = listenerYml.getKeys().iterator();
|
||||
Map<String, EaglerListenerConfig> serverListeners = new HashMap();
|
||||
Map<String, EaglerListenerConfig> serverListeners = new HashMap<>();
|
||||
boolean voiceChat = false;
|
||||
|
||||
while(listeners.hasNext()) {
|
||||
@ -119,6 +124,43 @@ public class EaglerBungeeConfig {
|
||||
}
|
||||
}
|
||||
|
||||
File pauseMenuFolder = new File(directory, "pause_menu");
|
||||
if(!pauseMenuFolder.isDirectory() && !pauseMenuFolder.mkdir()) {
|
||||
throw new IOException("Could not create directory: " + pauseMenuFolder.getAbsolutePath());
|
||||
}
|
||||
|
||||
File pauseMenuYml = new File(pauseMenuFolder, "pause_menu.yml");
|
||||
if(!pauseMenuYml.isFile()) {
|
||||
try(InputStream is = EaglerBungeeConfig.class.getResourceAsStream("default_pause_menu.yml")) {
|
||||
copyConfigFile(is, pauseMenuYml);
|
||||
}
|
||||
File f2 = new File(pauseMenuFolder, "server_info.html");
|
||||
if(!f2.isFile()) {
|
||||
try(InputStream is = EaglerBungeeConfig.class.getResourceAsStream("default_pause_menu_server_info.html")) {
|
||||
copyConfigFile(is, f2);
|
||||
}
|
||||
}
|
||||
f2 = new File(pauseMenuFolder, "test_image.png");
|
||||
if(!f2.isFile()) {
|
||||
try(InputStream is = EaglerBungeeConfig.class.getResourceAsStream("default_pause_menu_test_image.png")) {
|
||||
copyBinaryFile(is, f2);
|
||||
}
|
||||
}
|
||||
f2 = new File(pauseMenuFolder, "message_api_example.html");
|
||||
if(!f2.isFile()) {
|
||||
try(InputStream is = EaglerBungeeConfig.class.getResourceAsStream("default_message_api_example.html")) {
|
||||
copyConfigFile(is, f2);
|
||||
}
|
||||
}
|
||||
f2 = new File(pauseMenuFolder, "message_api_v1.js");
|
||||
if(!f2.isFile()) {
|
||||
try(InputStream is = EaglerBungeeConfig.class.getResourceAsStream("default_message_api_v1.js")) {
|
||||
copyConfigFile(is, f2);
|
||||
}
|
||||
}
|
||||
}
|
||||
EaglerPauseMenuConfig pauseMenuConfig = EaglerPauseMenuConfig.loadConfig(prov.load(pauseMenuYml), pauseMenuFolder);
|
||||
|
||||
long websocketKeepAliveTimeout = configYml.getInt("websocket_connection_timeout", 15000);
|
||||
long websocketHandshakeTimeout = configYml.getInt("websocket_handshake_timeout", 5000);
|
||||
long builtinHttpServerTimeout = configYml.getInt("builtin_http_server_timeout", 10000);
|
||||
@ -144,9 +186,10 @@ public class EaglerBungeeConfig {
|
||||
eaglerPlayersVanillaSkin = null;
|
||||
}
|
||||
boolean enableIsEaglerPlayerProperty = configYml.getBoolean("enable_is_eagler_player_property", true);
|
||||
Set<String> disableVoiceOnServers = new HashSet((Collection<String>)configYml.getList("disable_voice_chat_on_servers"));
|
||||
Set<String> disableVoiceOnServers = new HashSet<>((Collection<String>)configYml.getList("disable_voice_chat_on_servers"));
|
||||
boolean disableFNAWSkinsEverywhere = configYml.getBoolean("disable_fnaw_skins_everywhere", false);
|
||||
Set<String> disableFNAWSkinsOnServers = new HashSet((Collection<String>)configYml.getList("disable_fnaw_skins_on_servers"));
|
||||
Set<String> disableFNAWSkinsOnServers = new HashSet<>((Collection<String>)configYml.getList("disable_fnaw_skins_on_servers"));
|
||||
boolean enableBackendRPCAPI = configYml.getBoolean("enable_backend_rpc_api", false);
|
||||
|
||||
final EaglerBungeeConfig ret = new EaglerBungeeConfig(serverName, serverUUID, websocketKeepAliveTimeout,
|
||||
websocketHandshakeTimeout, builtinHttpServerTimeout, websocketCompressionLevel, serverListeners,
|
||||
@ -154,7 +197,7 @@ public class EaglerBungeeConfig {
|
||||
skinRateLimitPlayer, skinRateLimitGlobal, skinCacheURI, keepObjectsDays, keepProfilesDays, maxObjects,
|
||||
maxProfiles, antagonistsRateLimit, sqliteDriverClass, sqliteDriverPath, eaglerPlayersVanillaSkin,
|
||||
enableIsEaglerPlayerProperty, authConfig, updatesConfig, iceServers, voiceChat, disableVoiceOnServers,
|
||||
disableFNAWSkinsEverywhere, disableFNAWSkinsOnServers);
|
||||
disableFNAWSkinsEverywhere, disableFNAWSkinsOnServers, enableBackendRPCAPI, pauseMenuConfig);
|
||||
|
||||
if(eaglerPlayersVanillaSkin != null) {
|
||||
VanillaDefaultSkinProfileLoader.lookupVanillaSkinUser(ret);
|
||||
@ -168,7 +211,7 @@ public class EaglerBungeeConfig {
|
||||
if(!file.isFile()) {
|
||||
try (BufferedReader is = new BufferedReader(new InputStreamReader(
|
||||
EaglerBungeeConfig.class.getResourceAsStream("default_" + fileName), StandardCharsets.UTF_8));
|
||||
PrintWriter os = new PrintWriter(new FileWriter(file))) {
|
||||
PrintWriter os = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while((line = is.readLine()) != null) {
|
||||
if(line.contains("${")) {
|
||||
@ -181,6 +224,26 @@ public class EaglerBungeeConfig {
|
||||
return file;
|
||||
}
|
||||
|
||||
private static void copyConfigFile(InputStream is, File file) throws IOException {
|
||||
try(PrintWriter os = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
|
||||
String line;
|
||||
while((line = reader.readLine()) != null) {
|
||||
os.println(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void copyBinaryFile(InputStream is, File file) throws IOException {
|
||||
try(OutputStream os = new FileOutputStream(file)) {
|
||||
byte[] copyBuffer = new byte[1024];
|
||||
int i;
|
||||
while((i = is.read(copyBuffer)) != -1) {
|
||||
os.write(copyBuffer, 0, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadMimeTypes(InputStream file, Map<String, HttpContentType> contentTypes) throws IOException {
|
||||
JsonObject obj = parseJsonObject(file);
|
||||
for(Entry<String, JsonElement> etr : obj.entrySet()) {
|
||||
@ -192,7 +255,7 @@ public class EaglerBungeeConfig {
|
||||
EaglerXBungee.logger().warning("MIME type '" + mime + "' defines no extensions!");
|
||||
continue;
|
||||
}
|
||||
HashSet<String> exts = new HashSet();
|
||||
HashSet<String> exts = new HashSet<>();
|
||||
for(int i = 0, l = arr.size(); i < l; ++i) {
|
||||
exts.add(arr.get(i).getAsString());
|
||||
}
|
||||
@ -217,8 +280,8 @@ public class EaglerBungeeConfig {
|
||||
}
|
||||
|
||||
private static Collection<String> loadICEServers(Configuration config) {
|
||||
Collection<String> ret = new ArrayList(config.getList("voice_stun_servers"));
|
||||
Configuration turnServers = config.getSection("voice_turn_servers");
|
||||
Collection<String> ret = new ArrayList<>(config.contains("voice_stun_servers") ? (List<String>)config.getList("voice_stun_servers") : (List<String>)config.getList("voice_servers_no_passwd"));
|
||||
Configuration turnServers = config.contains("voice_turn_servers") ? config.getSection("voice_turn_servers") : config.getSection("voice_servers_passwd");
|
||||
Iterator<String> turnItr = turnServers.getKeys().iterator();
|
||||
while(turnItr.hasNext()) {
|
||||
String name = turnItr.next();
|
||||
@ -269,6 +332,8 @@ public class EaglerBungeeConfig {
|
||||
private final Set<String> disableVoiceOnServers;
|
||||
private final boolean disableFNAWSkinsEverywhere;
|
||||
private final Set<String> disableFNAWSkinsOnServers;
|
||||
private final boolean enableBackendRPCAPI;
|
||||
private final EaglerPauseMenuConfig pauseMenuConf;
|
||||
private boolean isCrackedFlag;
|
||||
Property[] eaglerPlayersVanillaSkinCached = new Property[] { isEaglerProperty };
|
||||
|
||||
@ -435,6 +500,14 @@ public class EaglerBungeeConfig {
|
||||
return disableFNAWSkinsOnServers;
|
||||
}
|
||||
|
||||
public boolean getEnableBackendRPCAPI() {
|
||||
return enableBackendRPCAPI;
|
||||
}
|
||||
|
||||
public EaglerPauseMenuConfig getPauseMenuConf() {
|
||||
return pauseMenuConf;
|
||||
}
|
||||
|
||||
private EaglerBungeeConfig(String serverName, UUID serverUUID, long websocketKeepAliveTimeout,
|
||||
long websocketHandshakeTimeout, long builtinHttpServerTimeout, int httpWebsocketCompressionLevel,
|
||||
Map<String, EaglerListenerConfig> serverListeners, Map<String, HttpContentType> contentTypes,
|
||||
@ -444,7 +517,8 @@ public class EaglerBungeeConfig {
|
||||
String sqliteDriverClass, String sqliteDriverPath, String eaglerPlayersVanillaSkin,
|
||||
boolean enableIsEaglerPlayerProperty, EaglerAuthConfig authConfig, EaglerUpdateConfig updateConfig,
|
||||
Collection<String> iceServers, boolean enableVoiceChat, Set<String> disableVoiceOnServers,
|
||||
boolean disableFNAWSkinsEverywhere, Set<String> disableFNAWSkinsOnServers) {
|
||||
boolean disableFNAWSkinsEverywhere, Set<String> disableFNAWSkinsOnServers,
|
||||
boolean enableBackendRPCAPI, EaglerPauseMenuConfig pauseMenuConf) {
|
||||
this.serverName = serverName;
|
||||
this.serverUUID = serverUUID;
|
||||
this.serverListeners = serverListeners;
|
||||
@ -476,6 +550,8 @@ public class EaglerBungeeConfig {
|
||||
this.disableVoiceOnServers = disableVoiceOnServers;
|
||||
this.disableFNAWSkinsEverywhere = disableFNAWSkinsEverywhere;
|
||||
this.disableFNAWSkinsOnServers = disableFNAWSkinsOnServers;
|
||||
this.enableBackendRPCAPI = enableBackendRPCAPI;
|
||||
this.pauseMenuConf = pauseMenuConf;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -74,8 +74,14 @@ public class EaglerListenerConfig extends ListenerInfo {
|
||||
for(int i = 0, l = serverMOTD.size(); i < l; ++i) {
|
||||
serverMOTD.set(i, ChatColor.translateAlternateColorCodes('&', serverMOTD.get(i)));
|
||||
}
|
||||
boolean allowMOTD = config.getBoolean("allow_motd", false);
|
||||
boolean allowQuery = config.getBoolean("allow_query", false);
|
||||
boolean allowMOTD = config.getBoolean("allow_motd", true);
|
||||
boolean allowQuery = config.getBoolean("allow_query", true);
|
||||
boolean allowV3 = config.getBoolean("allow_protocol_v3", true);
|
||||
boolean allowV4 = config.getBoolean("allow_protocol_v4", true);
|
||||
if(!allowV3 && !allowV4) {
|
||||
throw new IllegalArgumentException("Both v3 and v4 protocol are disabled!");
|
||||
}
|
||||
int defragSendDelay = config.getInt("protocol_v4_defrag_send_delay", 10);
|
||||
|
||||
int cacheTTL = 7200;
|
||||
boolean cacheAnimation = false;
|
||||
@ -102,8 +108,8 @@ public class EaglerListenerConfig extends ListenerInfo {
|
||||
page404 = null;
|
||||
}
|
||||
List<String> defaultIndex = Arrays.asList("index.html", "index.htm");
|
||||
List indexPageRaw = httpServerConf.getList("page_index_name", defaultIndex);
|
||||
List<String> indexPage = new ArrayList(indexPageRaw.size());
|
||||
List<?> indexPageRaw = httpServerConf.getList("page_index_name", defaultIndex);
|
||||
List<String> indexPage = new ArrayList<>(indexPageRaw.size());
|
||||
|
||||
for(int i = 0, l = indexPageRaw.size(); i < l; ++i) {
|
||||
Object o = indexPageRaw.get(i);
|
||||
@ -151,7 +157,8 @@ public class EaglerListenerConfig extends ListenerInfo {
|
||||
cacheTrending, cachePortfolios);
|
||||
return new EaglerListenerConfig(hostv4, hostv6, maxPlayer, tabListType, defaultServer, forceDefaultServer,
|
||||
forwardIp, forwardIpHeader, redirectLegacyClientsTo, serverIcon, serverMOTD, allowMOTD, allowQuery,
|
||||
cacheConfig, httpServer, enableVoiceChat, ratelimitIp, ratelimitLogin, ratelimitMOTD, ratelimitQuery);
|
||||
allowV3, allowV4, defragSendDelay, cacheConfig, httpServer, enableVoiceChat, ratelimitIp,
|
||||
ratelimitLogin, ratelimitMOTD, ratelimitQuery);
|
||||
}
|
||||
|
||||
private final InetSocketAddress address;
|
||||
@ -167,6 +174,9 @@ public class EaglerListenerConfig extends ListenerInfo {
|
||||
private final List<String> serverMOTD;
|
||||
private final boolean allowMOTD;
|
||||
private final boolean allowQuery;
|
||||
private final boolean allowV3;
|
||||
private final boolean allowV4;
|
||||
private final int defragSendDelay;
|
||||
private final MOTDCacheConfiguration motdCacheConfig;
|
||||
private final HttpWebServer webServer;
|
||||
private boolean serverIconSet = false;
|
||||
@ -180,9 +190,10 @@ public class EaglerListenerConfig extends ListenerInfo {
|
||||
public EaglerListenerConfig(InetSocketAddress address, InetSocketAddress addressV6, int maxPlayer,
|
||||
String tabListType, String defaultServer, boolean forceDefaultServer, boolean forwardIp,
|
||||
String forwardIpHeader, String redirectLegacyClientsTo, String serverIcon, List<String> serverMOTD,
|
||||
boolean allowMOTD, boolean allowQuery, MOTDCacheConfiguration motdCacheConfig, HttpWebServer webServer,
|
||||
boolean enableVoiceChat, EaglerRateLimiter ratelimitIp, EaglerRateLimiter ratelimitLogin,
|
||||
EaglerRateLimiter ratelimitMOTD, EaglerRateLimiter ratelimitQuery) {
|
||||
boolean allowMOTD, boolean allowQuery, boolean allowV3, boolean allowV4, int defragSendDelay,
|
||||
MOTDCacheConfiguration motdCacheConfig, HttpWebServer webServer, boolean enableVoiceChat,
|
||||
EaglerRateLimiter ratelimitIp, EaglerRateLimiter ratelimitLogin, EaglerRateLimiter ratelimitMOTD,
|
||||
EaglerRateLimiter ratelimitQuery) {
|
||||
super(address, String.join("\n", serverMOTD), maxPlayer, 60, Arrays.asList(defaultServer), forceDefaultServer,
|
||||
Collections.emptyMap(), tabListType, false, false, 0, false, false);
|
||||
this.address = address;
|
||||
@ -198,6 +209,9 @@ public class EaglerListenerConfig extends ListenerInfo {
|
||||
this.serverMOTD = serverMOTD;
|
||||
this.allowMOTD = allowMOTD;
|
||||
this.allowQuery = allowQuery;
|
||||
this.allowV3 = allowV3;
|
||||
this.allowV4 = allowV4;
|
||||
this.defragSendDelay = defragSendDelay;
|
||||
this.motdCacheConfig = motdCacheConfig;
|
||||
this.webServer = webServer;
|
||||
this.enableVoiceChat = enableVoiceChat;
|
||||
@ -272,7 +286,19 @@ public class EaglerListenerConfig extends ListenerInfo {
|
||||
public boolean isAllowQuery() {
|
||||
return allowQuery;
|
||||
}
|
||||
|
||||
|
||||
public boolean isAllowV3() {
|
||||
return allowV3;
|
||||
}
|
||||
|
||||
public boolean isAllowV4() {
|
||||
return allowV4;
|
||||
}
|
||||
|
||||
public int getDefragSendDelay() {
|
||||
return defragSendDelay;
|
||||
}
|
||||
|
||||
public HttpWebServer getWebServer() {
|
||||
return webServer;
|
||||
}
|
||||
|
@ -0,0 +1,251 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import gnu.trove.map.TIntObjectMap;
|
||||
import gnu.trove.map.hash.TIntObjectHashMap;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketCustomizePauseMenuV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketServerInfoDataChunkV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.PacketImageData;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SimpleOutputBufferImpl;
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class EaglerPauseMenuConfig {
|
||||
|
||||
private boolean enableCustomPauseMenu;
|
||||
private SPacketCustomizePauseMenuV4EAG customPauseMenuPacket;
|
||||
private byte[] serverInfoHash;
|
||||
private List<SPacketServerInfoDataChunkV4EAG> serverInfoChunks;
|
||||
private int infoSendRate;
|
||||
|
||||
static EaglerPauseMenuConfig loadConfig(Configuration conf, File baseDir) throws IOException {
|
||||
boolean enabled = conf.getBoolean("enable_custom_pause_menu", false);
|
||||
if(!enabled) {
|
||||
return new EaglerPauseMenuConfig(false, null, null, 1);
|
||||
}
|
||||
|
||||
Configuration server_info_button = conf.getSection("server_info_button");
|
||||
boolean enableInfoButton = server_info_button.getBoolean("enable_button", false);
|
||||
String infoButtonText = server_info_button.getString("button_text", "Server Info");
|
||||
boolean infoButtonModeNewTab = server_info_button.getBoolean("button_mode_open_new_tab", false);
|
||||
String infoButtonEmbedURL = server_info_button.getString("server_info_embed_url", "");
|
||||
boolean infoButtonModeEmbedFile = server_info_button.getBoolean("button_mode_embed_file", true);
|
||||
String infoButtonEmbedFile = server_info_button.getString("server_info_embed_file", "server_info.html");
|
||||
String infoButtonEmbedScreenTitle = server_info_button.getString("server_info_embed_screen_title", "Server Info");
|
||||
int infoSendRate = server_info_button.getInt("server_info_embed_send_chunk_rate", 1);
|
||||
int infoChunkSize = server_info_button.getInt("server_info_embed_send_chunk_size", 24576);
|
||||
if(infoChunkSize > 32720) {
|
||||
throw new IOException("Chunk size " +infoChunkSize + " is too large! Max is 32720 bytes");
|
||||
}
|
||||
boolean infoButtonEnableTemplateMacros = server_info_button.getBoolean("enable_template_macros", true);
|
||||
Configuration globals = server_info_button.getSection("server_info_embed_template_globals");
|
||||
for(String s : globals.getKeys()) {
|
||||
EaglerXBungeeAPIHelper.getTemplateGlobals().put(s, globals.getString(s));
|
||||
}
|
||||
boolean infoButtonAllowTemplateEvalMacro = server_info_button.getBoolean("allow_embed_template_eval_macro", false);
|
||||
boolean infoButtonEnableWebviewJavascript = server_info_button.getBoolean("enable_webview_javascript", false);
|
||||
boolean infoButtonEnableWebviewMessageAPI = server_info_button.getBoolean("enable_webview_message_api", false);
|
||||
boolean infoButtonEnableWebviewStrictCSP = server_info_button.getBoolean("enable_webview_strict_csp", true);
|
||||
|
||||
Configuration discord_button = conf.getSection("discord_button");
|
||||
boolean enableDiscordButton = discord_button.getBoolean("enable_button", false);
|
||||
String discordButtonText = discord_button.getString("button_text", "Discord");
|
||||
String discordButtonURL = discord_button.getString("button_url", "https://invite url here");
|
||||
|
||||
int infoButtonMode = enableInfoButton
|
||||
? (infoButtonModeEmbedFile
|
||||
? (infoButtonEmbedFile.length() > 0
|
||||
? SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_SHOW_EMBED_OVER_WS
|
||||
: SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_NONE)
|
||||
: (infoButtonEmbedURL.length() > 0
|
||||
? (infoButtonModeNewTab ? SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_EXTERNAL_URL
|
||||
: SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_SHOW_EMBED_OVER_HTTP)
|
||||
: SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_NONE))
|
||||
: SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_NONE;
|
||||
|
||||
int discordButtonMode = (enableDiscordButton && discordButtonURL.length() > 0)
|
||||
? SPacketCustomizePauseMenuV4EAG.DISCORD_MODE_INVITE_URL
|
||||
: SPacketCustomizePauseMenuV4EAG.DISCORD_MODE_NONE;
|
||||
|
||||
int webviewPerms = (infoButtonEnableWebviewJavascript ? SPacketCustomizePauseMenuV4EAG.SERVER_INFO_EMBED_PERMS_JAVASCRIPT : 0) |
|
||||
(infoButtonEnableWebviewMessageAPI ? SPacketCustomizePauseMenuV4EAG.SERVER_INFO_EMBED_PERMS_MESSAGE_API : 0) |
|
||||
(infoButtonEnableWebviewStrictCSP ? SPacketCustomizePauseMenuV4EAG.SERVER_INFO_EMBED_PERMS_STRICT_CSP : 0);
|
||||
|
||||
Map<String,String> imagesToActuallyLoad = new WeakHashMap<>();
|
||||
|
||||
Configuration custom_images = conf.getSection("custom_images");
|
||||
for(String s : custom_images.getKeys()) {
|
||||
String fileName = custom_images.getString(s, "");
|
||||
if(fileName.length() > 0) {
|
||||
imagesToActuallyLoad.put(s, fileName);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String,Integer> imageMappings = null;
|
||||
List<PacketImageData> customImageDatas = null;
|
||||
|
||||
if(imagesToActuallyLoad.size() > 0) {
|
||||
Map<String,PacketImageData> imageLoadingCache = new HashMap<>();
|
||||
TIntObjectMap<PacketImageData> imageDumbHashTable = new TIntObjectHashMap<>();
|
||||
|
||||
imageMappings = new HashMap<>();
|
||||
customImageDatas = new ArrayList<>();
|
||||
|
||||
outer_loop: for(Entry<String,String> etr : imagesToActuallyLoad.entrySet()) {
|
||||
String key = etr.getKey();
|
||||
String value = etr.getValue();
|
||||
PacketImageData existing = imageLoadingCache.get(value);
|
||||
if(existing != null) {
|
||||
for(int i = 0, l = customImageDatas.size(); i < l; ++i) {
|
||||
if(customImageDatas.get(i) == existing) {
|
||||
imageMappings.put(key, i);
|
||||
continue outer_loop;
|
||||
}
|
||||
}
|
||||
imageMappings.put(key, customImageDatas.size());
|
||||
customImageDatas.add(existing);
|
||||
continue outer_loop;
|
||||
}else {
|
||||
PacketImageData img = EaglerXBungeeAPIHelper.loadPacketImageData(new File(baseDir, value), 64, 64);
|
||||
int hashCode = Arrays.hashCode(img.rgba);
|
||||
PacketImageData possibleClone = imageDumbHashTable.get(hashCode);
|
||||
if (possibleClone != null && possibleClone.width == img.width && possibleClone.height == img.height
|
||||
&& Arrays.equals(img.rgba, possibleClone.rgba)) {
|
||||
for(int i = 0, l = customImageDatas.size(); i < l; ++i) {
|
||||
if(customImageDatas.get(i) == possibleClone) {
|
||||
imageMappings.put(key, i);
|
||||
continue outer_loop;
|
||||
}
|
||||
}
|
||||
imageMappings.put(key, customImageDatas.size());
|
||||
customImageDatas.add(possibleClone);
|
||||
continue outer_loop;
|
||||
}else {
|
||||
imageMappings.put(key, customImageDatas.size());
|
||||
customImageDatas.add(img);
|
||||
imageDumbHashTable.put(hashCode, img);
|
||||
continue outer_loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SPacketCustomizePauseMenuV4EAG pausePacket = new SPacketCustomizePauseMenuV4EAG();
|
||||
List<SPacketServerInfoDataChunkV4EAG> serverInfoChunks = null;
|
||||
|
||||
pausePacket.serverInfoMode = infoButtonMode;
|
||||
switch(infoButtonMode) {
|
||||
case SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_NONE:
|
||||
default:
|
||||
break;
|
||||
case SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_EXTERNAL_URL:
|
||||
pausePacket.serverInfoButtonText = infoButtonText;
|
||||
pausePacket.serverInfoURL = infoButtonEmbedURL;
|
||||
break;
|
||||
case SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_SHOW_EMBED_OVER_HTTP:
|
||||
pausePacket.serverInfoButtonText = infoButtonText;
|
||||
pausePacket.serverInfoURL = infoButtonEmbedURL;
|
||||
pausePacket.serverInfoEmbedPerms = webviewPerms;
|
||||
pausePacket.serverInfoEmbedTitle = infoButtonEmbedScreenTitle;
|
||||
break;
|
||||
case SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_SHOW_EMBED_OVER_WS:
|
||||
pausePacket.serverInfoButtonText = infoButtonText;
|
||||
byte[] hash = new byte[20];
|
||||
String rawData = EaglerXBungeeAPIHelper.loadFileToStringServerInfo(new File(baseDir, infoButtonEmbedFile));
|
||||
if(infoButtonEnableTemplateMacros) {
|
||||
rawData = EaglerXBungeeAPIHelper.loadServerInfoTemplateEagler(rawData, baseDir, infoButtonAllowTemplateEvalMacro);
|
||||
}
|
||||
serverInfoChunks = EaglerXBungeeAPIHelper.convertServerInfoToChunks(rawData.getBytes(StandardCharsets.UTF_8), hash, infoChunkSize);
|
||||
if(!serverInfoChunks.isEmpty()) {
|
||||
SPacketServerInfoDataChunkV4EAG pk = serverInfoChunks.get(0);
|
||||
EaglerXBungee.logger().info("Total server info embed size: " + pk.finalSize + " bytes" + (serverInfoChunks.size() > 1 ? (" (" + serverInfoChunks.size() + " chunks)") : ""));
|
||||
}
|
||||
pausePacket.serverInfoEmbedPerms = webviewPerms;
|
||||
pausePacket.serverInfoEmbedTitle = infoButtonEmbedScreenTitle;
|
||||
pausePacket.serverInfoHash = hash;
|
||||
break;
|
||||
}
|
||||
|
||||
pausePacket.discordButtonMode = discordButtonMode;
|
||||
switch(discordButtonMode) {
|
||||
case SPacketCustomizePauseMenuV4EAG.DISCORD_MODE_NONE:
|
||||
default:
|
||||
break;
|
||||
case SPacketCustomizePauseMenuV4EAG.DISCORD_MODE_INVITE_URL:
|
||||
pausePacket.discordButtonMode = discordButtonMode;
|
||||
pausePacket.discordButtonText = discordButtonText;
|
||||
pausePacket.discordInviteURL = discordButtonURL;
|
||||
break;
|
||||
}
|
||||
|
||||
pausePacket.imageMappings = imageMappings;
|
||||
pausePacket.imageData = customImageDatas;
|
||||
|
||||
SimpleOutputBufferImpl ob = new SimpleOutputBufferImpl(new TestOutputStream());
|
||||
pausePacket.writePacket(ob);
|
||||
int cnt = ob.size();
|
||||
|
||||
EaglerXBungee.logger().info("Total pause menu packet size: " + cnt + " bytes");
|
||||
if(cnt > 32760) {
|
||||
throw new IOException("Pause menu packet is " + (cnt - 32760) + " bytes too large! Try making the images smaller or reusing the same image file for multiple icons!");
|
||||
}
|
||||
|
||||
return new EaglerPauseMenuConfig(enabled, pausePacket, serverInfoChunks, infoSendRate);
|
||||
}
|
||||
|
||||
private EaglerPauseMenuConfig(boolean enableCustomPauseMenu, SPacketCustomizePauseMenuV4EAG customPauseMenuPacket,
|
||||
List<SPacketServerInfoDataChunkV4EAG> serverInfoChunks, int infoSendRate) {
|
||||
this.enableCustomPauseMenu = enableCustomPauseMenu;
|
||||
this.customPauseMenuPacket = customPauseMenuPacket;
|
||||
this.serverInfoHash = customPauseMenuPacket != null ? customPauseMenuPacket.serverInfoHash : null;
|
||||
this.serverInfoChunks = serverInfoChunks;
|
||||
this.infoSendRate = infoSendRate;
|
||||
}
|
||||
|
||||
public boolean getEnabled() {
|
||||
return enableCustomPauseMenu;
|
||||
}
|
||||
|
||||
public SPacketCustomizePauseMenuV4EAG getPacket() {
|
||||
return customPauseMenuPacket;
|
||||
}
|
||||
|
||||
public byte[] getServerInfoHash() {
|
||||
return serverInfoHash;
|
||||
}
|
||||
|
||||
public List<SPacketServerInfoDataChunkV4EAG> getServerInfo() {
|
||||
return serverInfoChunks;
|
||||
}
|
||||
|
||||
public int getInfoSendRate() {
|
||||
return infoSendRate;
|
||||
}
|
||||
|
||||
}
|
@ -7,6 +7,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
|
||||
/**
|
||||
@ -95,7 +96,7 @@ public class EaglerRateLimiter {
|
||||
protected long cooldownTimestamp = 0l;
|
||||
|
||||
protected RateLimitStatus rateLimit() {
|
||||
long millis = System.currentTimeMillis();
|
||||
long millis = EaglerXBungeeAPIHelper.steadyTimeMillis();
|
||||
tick(millis);
|
||||
if(lockoutTimestamp != 0l) {
|
||||
return RateLimitStatus.LOCKED_OUT;
|
||||
@ -136,7 +137,7 @@ public class EaglerRateLimiter {
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<String, RateLimiter> ratelimiters = new HashMap();
|
||||
private final Map<String, RateLimiter> ratelimiters = new HashMap<>();
|
||||
|
||||
public RateLimitStatus rateLimit(String addr) {
|
||||
addr = addr.toLowerCase();
|
||||
@ -156,7 +157,7 @@ public class EaglerRateLimiter {
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
long millis = System.currentTimeMillis();
|
||||
long millis = EaglerXBungeeAPIHelper.steadyTimeMillis();
|
||||
synchronized(ratelimiters) {
|
||||
Iterator<RateLimiter> itr = ratelimiters.values().iterator();
|
||||
while(itr.hasNext()) {
|
||||
@ -181,7 +182,7 @@ public class EaglerRateLimiter {
|
||||
int limitLockout = config.getInt("limit_lockout", -1);
|
||||
int lockoutDuration = config.getInt("lockout_duration", -1);
|
||||
Collection<String> exc = (Collection<String>) config.getList("exceptions");
|
||||
List<String> exceptions = new ArrayList();
|
||||
List<String> exceptions = new ArrayList<>();
|
||||
for(String str : exc) {
|
||||
exceptions.add(str.toLowerCase());
|
||||
}
|
||||
|
@ -0,0 +1,250 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import com.google.common.html.HtmlEscapers;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.repackage.lang3.StrTokenizer;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class ServerInfoTemplateParser {
|
||||
|
||||
private static final Gson jsonEscaper = (new GsonBuilder()).disableHtmlEscaping().create();
|
||||
|
||||
private static class State {
|
||||
private boolean evalAllowed;
|
||||
private File baseDir;
|
||||
private Map<String, String> globals;
|
||||
private boolean htmlEscape;
|
||||
private boolean strEscape;
|
||||
private boolean disableMacros;
|
||||
private boolean enableEval;
|
||||
private State(File baseDir, boolean evalAllowed, Map<String, String> globals) {
|
||||
this.baseDir = baseDir;
|
||||
this.evalAllowed = evalAllowed;
|
||||
this.globals = globals;
|
||||
}
|
||||
private State push() {
|
||||
return new State(baseDir, evalAllowed, globals);
|
||||
}
|
||||
}
|
||||
|
||||
public static String loadTemplate(String content, File baseDir, boolean evalAllowed, Map<String, String> globals) throws IOException {
|
||||
return loadTemplate(content, new State(baseDir, evalAllowed, globals));
|
||||
}
|
||||
|
||||
private static String loadTemplate(String content, State state) throws IOException {
|
||||
StringBuilder ret = new StringBuilder();
|
||||
int i = 0, j = 0;
|
||||
while((i = content.indexOf("{%", j)) != -1) {
|
||||
ret.append(content, j, i);
|
||||
j = i;
|
||||
i = content.indexOf("%}", j + 2);
|
||||
if(i != -1) {
|
||||
ret.append(processMacro(content.substring(j + 2, i), state));
|
||||
j = i + 2;
|
||||
}else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret.append(content, j, content.length());
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
public static class InvalidMacroException extends RuntimeException {
|
||||
|
||||
public InvalidMacroException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public InvalidMacroException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static String processMacro(String content, State state) throws IOException {
|
||||
String trimmed = content.trim();
|
||||
try {
|
||||
String[] strs = (new StrTokenizer(trimmed, ' ', '`')).getTokenArray();
|
||||
if(strs.length < 1) {
|
||||
return "{%" + content + "%}";
|
||||
}
|
||||
if(strs[0].equals("disablemacros") && strs.length == 2) {
|
||||
switch(strs[1]) {
|
||||
case "on":
|
||||
if(state.disableMacros) {
|
||||
return "{%" + content + "%}";
|
||||
}else {
|
||||
state.disableMacros = true;
|
||||
return "";
|
||||
}
|
||||
case "off":
|
||||
state.disableMacros = false;
|
||||
return "";
|
||||
default:
|
||||
if(state.disableMacros) {
|
||||
return "{%" + content + "%}";
|
||||
}else {
|
||||
throw new InvalidMacroException("Unknown disablemacros mode: " + strs[1] + " (Expected: on, off)");
|
||||
}
|
||||
}
|
||||
}else if(!state.disableMacros) {
|
||||
switch(strs[0]) {
|
||||
case "embed":
|
||||
argCheck(3, strs.length);
|
||||
switch(strs[1]) {
|
||||
case "base64":
|
||||
return Base64.encodeBase64String(EaglerXBungeeAPIHelper.loadFileToByteArrayServerInfo(new File(state.baseDir, strs[2])));
|
||||
case "text":
|
||||
return escapeMacroResult(EaglerXBungeeAPIHelper.loadFileToStringServerInfo(new File(state.baseDir, strs[2])), state);
|
||||
case "eval":
|
||||
if(state.evalAllowed) {
|
||||
return escapeMacroResult(loadTemplate(EaglerXBungeeAPIHelper.loadFileToStringServerInfo(new File(state.baseDir, strs[2])), state.push()), state);
|
||||
}else {
|
||||
throw new InvalidMacroException("Template tried to eval file \"" + strs[2] + "\"! (eval is disabled)");
|
||||
}
|
||||
default:
|
||||
throw new InvalidMacroException("Unknown embed mode: " + strs[1] + " (Expected: base64, text, eval)");
|
||||
}
|
||||
case "htmlescape":
|
||||
argCheck(2, strs.length);
|
||||
switch(strs[1]) {
|
||||
case "on":
|
||||
state.htmlEscape = true;
|
||||
return "";
|
||||
case "off":
|
||||
state.htmlEscape = false;
|
||||
return "";
|
||||
default:
|
||||
throw new InvalidMacroException("Unknown htmlescape mode: " + strs[1] + " (Expected: on, off)");
|
||||
}
|
||||
case "strescape":
|
||||
argCheck(2, strs.length);
|
||||
switch(strs[1]) {
|
||||
case "on":
|
||||
state.strEscape = true;
|
||||
return "";
|
||||
case "off":
|
||||
state.strEscape = false;
|
||||
return "";
|
||||
default:
|
||||
throw new InvalidMacroException("Unknown strescape mode: " + strs[1] + " (Expected: on, off)");
|
||||
}
|
||||
case "eval":
|
||||
argCheck(2, strs.length);
|
||||
switch(strs[1]) {
|
||||
case "on":
|
||||
if(!state.evalAllowed) {
|
||||
throw new InvalidMacroException("Template tried to enable eval! (eval is disabled)");
|
||||
}
|
||||
state.enableEval = true;
|
||||
return "";
|
||||
case "off":
|
||||
state.enableEval = false;
|
||||
return "";
|
||||
default:
|
||||
throw new InvalidMacroException("Unknown eval mode: " + strs[1] + " (Expected: on, off)");
|
||||
}
|
||||
case "global":
|
||||
argCheck(2, 3, strs.length);
|
||||
String ret = state.globals.get(strs[1]);
|
||||
if(ret == null) {
|
||||
if(strs.length == 3) {
|
||||
ret = strs[2];
|
||||
}else {
|
||||
throw new InvalidMacroException("Unknown global \"" + strs[1] + "\"! (Available: " + String.join(", ", state.globals.keySet()) + ")");
|
||||
}
|
||||
}
|
||||
return escapeMacroResult(ret, state);
|
||||
case "property":
|
||||
argCheck(2, 3, strs.length);
|
||||
ret = System.getProperty(strs[1]);
|
||||
if(ret == null) {
|
||||
if(strs.length == 3) {
|
||||
ret = strs[2];
|
||||
}else {
|
||||
throw new InvalidMacroException("Unknown system property \"" + strs[1] + "\"!");
|
||||
}
|
||||
}
|
||||
return escapeMacroResult(ret, state);
|
||||
case "text":
|
||||
argCheck(2, strs.length);
|
||||
return escapeMacroResult(strs[1], state);
|
||||
case "translate":
|
||||
argCheckMin(2, strs.length);
|
||||
String[] additionalArgs = new String[strs.length - 2];
|
||||
System.arraycopy(strs, 2, additionalArgs, 0, additionalArgs.length);
|
||||
return escapeMacroResult(BungeeCord.getInstance().getTranslation(strs[1], (Object[])additionalArgs), state);
|
||||
default:
|
||||
return "{%" + content + "%}";
|
||||
}
|
||||
}else {
|
||||
return "{%" + content + "%}";
|
||||
}
|
||||
}catch(InvalidMacroException ex) {
|
||||
throw new IOException("Invalid macro: {% " + trimmed + " %}, message: " + ex.getMessage(), ex);
|
||||
}catch(Throwable th) {
|
||||
throw new IOException("Error processing: {% " + trimmed + " %}, raised: " + th.toString(), th);
|
||||
}
|
||||
}
|
||||
|
||||
private static String escapeMacroResult(String str, State state) throws IOException {
|
||||
if(str.length() > 0) {
|
||||
if(state.evalAllowed && state.enableEval) {
|
||||
str = loadTemplate(str, state.push());
|
||||
}
|
||||
if(state.strEscape) {
|
||||
str = jsonEscaper.toJson(str);
|
||||
if(str.length() >= 2) {
|
||||
str = str.substring(1, str.length() - 1);
|
||||
}
|
||||
}
|
||||
if(state.htmlEscape) {
|
||||
str = HtmlEscapers.htmlEscaper().escape(str);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
private static void argCheck(int expect, int actual) {
|
||||
if(expect != actual) {
|
||||
throw new InvalidMacroException("Wrong number of arguments (" + actual + ", expected " + expect + ")");
|
||||
}
|
||||
}
|
||||
|
||||
private static void argCheck(int expectMin, int expectMax, int actual) {
|
||||
if(expectMin > actual || expectMax < actual) {
|
||||
throw new InvalidMacroException("Wrong number of arguments (" + actual + ", expected " + expectMin + " to " + expectMax + ")");
|
||||
}
|
||||
}
|
||||
|
||||
private static void argCheckMin(int expectMin, int actual) {
|
||||
if(expectMin > actual) {
|
||||
throw new InvalidMacroException("Wrong number of arguments (expected " + expectMin + " or more, got " + actual + ")");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,9 +1,10 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.bungeeprotocol;
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config;
|
||||
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2023 lax1dude. All Rights Reserved.
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
@ -17,16 +18,14 @@ import net.md_5.bungee.protocol.DefinedPacket;
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class EaglerProtocolAccessProxy {
|
||||
|
||||
public static int getPacketId(EaglerBungeeProtocol protocol, int protocolVersion, DefinedPacket pkt, boolean server) {
|
||||
final EaglerBungeeProtocol.DirectionData prot = server ? protocol.TO_CLIENT : protocol.TO_SERVER;
|
||||
return prot.getId((Class) pkt.getClass(), protocolVersion);
|
||||
public class TestOutputStream extends OutputStream {
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
}
|
||||
|
||||
public static DefinedPacket createPacket(EaglerBungeeProtocol protocol, int protocolVersion, int packetId, boolean server) {
|
||||
final EaglerBungeeProtocol.DirectionData prot = server ? protocol.TO_CLIENT : protocol.TO_SERVER;
|
||||
return prot.createPacket(packetId, protocolVersion);
|
||||
@Override
|
||||
public void write(byte[] b, int o, int l) throws IOException {
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.handlers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
@ -11,20 +10,18 @@ import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.EaglerBackendRPCProtocol;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.auth.DefaultAuthSystem;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerAuthConfig;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.CapePackets;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.CapeServiceOffline;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerPipeline;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.backend_rpc_protocol.BackendRPCSessionHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.protocol.GameProtocolMessageController;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SkinPackets;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SkinService;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.voice.VoiceService;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.voice.VoiceSignalPackets;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.connection.Server;
|
||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||
@ -39,7 +36,7 @@ import net.md_5.bungee.event.EventHandler;
|
||||
import net.md_5.bungee.protocol.Property;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2023 lax1dude. All Rights Reserved.
|
||||
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
@ -55,7 +52,6 @@ import net.md_5.bungee.protocol.Property;
|
||||
*/
|
||||
public class EaglerPacketEventListener implements Listener {
|
||||
|
||||
public static final String FNAW_SKIN_ENABLE_CHANNEL = "EAG|FNAWSEn-1.8";
|
||||
public static final String GET_DOMAIN_CHANNEL = "EAG|GetDomain";
|
||||
|
||||
public final EaglerXBungee plugin;
|
||||
@ -68,50 +64,64 @@ public class EaglerPacketEventListener implements Listener {
|
||||
public void onPluginMessage(final PluginMessageEvent event) {
|
||||
if(event.getSender() instanceof UserConnection) {
|
||||
final UserConnection player = (UserConnection)event.getSender();
|
||||
String tag = event.getTag();
|
||||
if(player.getPendingConnection() instanceof EaglerInitialHandler) {
|
||||
if(SkinService.CHANNEL.equals(event.getTag())) {
|
||||
event.setCancelled(true);
|
||||
ProxyServer.getInstance().getScheduler().runAsync(plugin, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
SkinPackets.processPacket(event.getData(), player, plugin.getSkinService());
|
||||
} catch (IOException e) {
|
||||
event.getSender().disconnect(new TextComponent("Skin packet error!"));
|
||||
EaglerXBungee.logger().log(Level.SEVERE, "Eagler user \"" + player.getName() + "\" raised an exception handling skins!", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}else if(CapeServiceOffline.CHANNEL.equals(event.getTag())) {
|
||||
event.setCancelled(true);
|
||||
EaglerInitialHandler initialHandler = (EaglerInitialHandler)player.getPendingConnection();
|
||||
GameProtocolMessageController msgController = initialHandler.getEaglerMessageController();
|
||||
if(msgController != null) {
|
||||
try {
|
||||
CapePackets.processPacket(event.getData(), player, plugin.getCapeService());
|
||||
} catch (IOException e) {
|
||||
event.getSender().disconnect(new TextComponent("Cape packet error!"));
|
||||
EaglerXBungee.logger().log(Level.SEVERE, "Eagler user \"" + player.getName() + "\" raised an exception handling capes!", e);
|
||||
}
|
||||
}else if(VoiceService.CHANNEL.equals(event.getTag())) {
|
||||
event.setCancelled(true);
|
||||
VoiceService svc = plugin.getVoiceService();
|
||||
if(svc != null && ((EaglerInitialHandler)player.getPendingConnection()).getEaglerListenerConfig().getEnableVoiceChat()) {
|
||||
try {
|
||||
VoiceSignalPackets.processPacket(event.getData(), player, svc);
|
||||
} catch (IOException e) {
|
||||
event.getSender().disconnect(new TextComponent("Voice signal packet error!"));
|
||||
EaglerXBungee.logger().log(Level.SEVERE, "Eagler user \"" + player.getName() + "\" raised an exception handling voice signals!", e);
|
||||
if(msgController.handlePacket(tag, event.getData())) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
event.getSender().disconnect(new TextComponent("Eaglercraft packet error!"));
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(tag.equals(EaglerBackendRPCProtocol.CHANNEL_NAME)) {
|
||||
event.getSender().disconnect(new TextComponent("Nope!"));
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
if(tag.equals(EaglerBackendRPCProtocol.CHANNEL_NAME_READY)) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}else if(event.getSender() instanceof Server && event.getReceiver() instanceof UserConnection) {
|
||||
UserConnection player = (UserConnection)event.getReceiver();
|
||||
if(GET_DOMAIN_CHANNEL.equals(event.getTag()) && player.getPendingConnection() instanceof EaglerInitialHandler) {
|
||||
event.setCancelled(true);
|
||||
String domain = ((EaglerInitialHandler)player.getPendingConnection()).getOrigin();
|
||||
if(domain == null) {
|
||||
((Server)event.getSender()).sendData("EAG|Domain", new byte[] { 0 });
|
||||
}else {
|
||||
((Server)event.getSender()).sendData("EAG|Domain", domain.getBytes(StandardCharsets.UTF_8));
|
||||
String tag = event.getTag();
|
||||
if(player.getPendingConnection() instanceof EaglerInitialHandler) {
|
||||
EaglerInitialHandler initialHandler = (EaglerInitialHandler)player.getPendingConnection();
|
||||
if(EaglerBackendRPCProtocol.CHANNEL_NAME.equals(tag)) {
|
||||
event.setCancelled(true);
|
||||
try {
|
||||
initialHandler.handleBackendRPCPacket((Server)event.getSender(), event.getData());
|
||||
}catch(Throwable t) {
|
||||
EaglerXBungee.logger().log(Level.SEVERE, "[" + ((UserConnection) event.getReceiver()).getName()
|
||||
+ "]: Caught an exception handling backend RPC packet!", t);
|
||||
}
|
||||
}else if(GET_DOMAIN_CHANNEL.equals(tag)) {
|
||||
event.setCancelled(true);
|
||||
String domain = initialHandler.getOrigin();
|
||||
if(domain != null) {
|
||||
((Server)event.getSender()).sendData("EAG|Domain", domain.getBytes(StandardCharsets.UTF_8));
|
||||
}else {
|
||||
((Server)event.getSender()).sendData("EAG|Domain", new byte[] { 0 });
|
||||
}
|
||||
}
|
||||
}else {
|
||||
if(EaglerBackendRPCProtocol.CHANNEL_NAME.equals(tag)) {
|
||||
event.setCancelled(true);
|
||||
try {
|
||||
BackendRPCSessionHandler.handlePacketOnVanilla((Server) event.getSender(),
|
||||
(UserConnection) event.getReceiver(), event.getData());
|
||||
}catch(Throwable t) {
|
||||
EaglerXBungee.logger().log(Level.SEVERE, "[" + ((UserConnection) event.getReceiver()).getName()
|
||||
+ "]: Caught an exception handling backend RPC packet!", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -175,19 +185,7 @@ public class EaglerPacketEventListener implements Listener {
|
||||
@EventHandler
|
||||
public void onServerConnected(ServerConnectedEvent event) {
|
||||
if(event.getPlayer() instanceof UserConnection) {
|
||||
UserConnection player = (UserConnection)event.getPlayer();
|
||||
if(player.getPendingConnection() instanceof EaglerInitialHandler) {
|
||||
EaglerInitialHandler handler = (EaglerInitialHandler) player.getPendingConnection();
|
||||
ServerInfo sv = event.getServer().getInfo();
|
||||
boolean fnawSkins = !plugin.getConfig().getDisableFNAWSkinsEverywhere() && !plugin.getConfig().getDisableFNAWSkinsOnServersSet().contains(sv.getName());
|
||||
if(fnawSkins != handler.currentFNAWSkinEnableStatus) {
|
||||
handler.currentFNAWSkinEnableStatus = fnawSkins;
|
||||
player.sendData(FNAW_SKIN_ENABLE_CHANNEL, new byte[] { fnawSkins ? (byte)1 : (byte)0 });
|
||||
}
|
||||
if(handler.getEaglerListenerConfig().getEnableVoiceChat()) {
|
||||
plugin.getVoiceService().handleServerConnected(player, sv);
|
||||
}
|
||||
}
|
||||
EaglerPipeline.addServerConnectListener((UserConnection)event.getPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,10 +193,15 @@ public class EaglerPacketEventListener implements Listener {
|
||||
public void onServerDisconnected(ServerDisconnectEvent event) {
|
||||
if(event.getPlayer() instanceof UserConnection) {
|
||||
UserConnection player = (UserConnection)event.getPlayer();
|
||||
if((player.getPendingConnection() instanceof EaglerInitialHandler)
|
||||
&& ((EaglerInitialHandler) player.getPendingConnection()).getEaglerListenerConfig()
|
||||
.getEnableVoiceChat()) {
|
||||
plugin.getVoiceService().handleServerDisconnected(player, event.getTarget());
|
||||
if(player.getPendingConnection() instanceof EaglerInitialHandler) {
|
||||
EaglerInitialHandler handler = (EaglerInitialHandler) player.getPendingConnection();
|
||||
BackendRPCSessionHandler rpcHandler = handler.getRPCSessionHandler();
|
||||
if(rpcHandler != null) {
|
||||
rpcHandler.handleConnectionLost(event.getTarget());
|
||||
}
|
||||
if(handler.getEaglerListenerConfig().getEnableVoiceChat()) {
|
||||
plugin.getVoiceService().handleServerDisconnected(player, event.getTarget());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,439 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.repackage.lang3;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.commons.lang3.ArraySorter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* A matcher class that can be queried to determine if a character array portion
|
||||
* matches.
|
||||
* <p>
|
||||
* This class comes complete with various factory methods. If these do not
|
||||
* suffice, you can subclass and implement your own matcher.
|
||||
*
|
||||
* @since 2.2
|
||||
* @!deprecated as of 3.6, use commons-text <a href=
|
||||
* "https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/matcher/StringMatcherFactory.html">
|
||||
* StringMatcherFactory</a> instead
|
||||
*/
|
||||
//@Deprecated
|
||||
public abstract class StrMatcher {
|
||||
|
||||
/**
|
||||
* Matches the comma character.
|
||||
*/
|
||||
private static final StrMatcher COMMA_MATCHER = new CharMatcher(',');
|
||||
/**
|
||||
* Matches the tab character.
|
||||
*/
|
||||
private static final StrMatcher TAB_MATCHER = new CharMatcher('\t');
|
||||
/**
|
||||
* Matches the space character.
|
||||
*/
|
||||
private static final StrMatcher SPACE_MATCHER = new CharMatcher(' ');
|
||||
/**
|
||||
* Matches the same characters as StringTokenizer, namely space, tab, newline,
|
||||
* formfeed.
|
||||
*/
|
||||
private static final StrMatcher SPLIT_MATCHER = new CharSetMatcher(" \t\n\r\f".toCharArray());
|
||||
/**
|
||||
* Matches the String trim() whitespace characters.
|
||||
*/
|
||||
private static final StrMatcher TRIM_MATCHER = new TrimMatcher();
|
||||
/**
|
||||
* Matches the double quote character.
|
||||
*/
|
||||
private static final StrMatcher SINGLE_QUOTE_MATCHER = new CharMatcher('\'');
|
||||
/**
|
||||
* Matches the double quote character.
|
||||
*/
|
||||
private static final StrMatcher DOUBLE_QUOTE_MATCHER = new CharMatcher('"');
|
||||
/**
|
||||
* Matches the single or double quote character.
|
||||
*/
|
||||
private static final StrMatcher QUOTE_MATCHER = new CharSetMatcher("'\"".toCharArray());
|
||||
/**
|
||||
* Matches no characters.
|
||||
*/
|
||||
private static final StrMatcher NONE_MATCHER = new NoMatcher();
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a matcher which matches the comma character.
|
||||
*
|
||||
* @return a matcher for a comma
|
||||
*/
|
||||
public static StrMatcher commaMatcher() {
|
||||
return COMMA_MATCHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a matcher which matches the tab character.
|
||||
*
|
||||
* @return a matcher for a tab
|
||||
*/
|
||||
public static StrMatcher tabMatcher() {
|
||||
return TAB_MATCHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a matcher which matches the space character.
|
||||
*
|
||||
* @return a matcher for a space
|
||||
*/
|
||||
public static StrMatcher spaceMatcher() {
|
||||
return SPACE_MATCHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the same characters as StringTokenizer, namely space, tab, newline
|
||||
* and formfeed.
|
||||
*
|
||||
* @return the split matcher
|
||||
*/
|
||||
public static StrMatcher splitMatcher() {
|
||||
return SPLIT_MATCHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the String trim() whitespace characters.
|
||||
*
|
||||
* @return the trim matcher
|
||||
*/
|
||||
public static StrMatcher trimMatcher() {
|
||||
return TRIM_MATCHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a matcher which matches the single quote character.
|
||||
*
|
||||
* @return a matcher for a single quote
|
||||
*/
|
||||
public static StrMatcher singleQuoteMatcher() {
|
||||
return SINGLE_QUOTE_MATCHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a matcher which matches the double quote character.
|
||||
*
|
||||
* @return a matcher for a double quote
|
||||
*/
|
||||
public static StrMatcher doubleQuoteMatcher() {
|
||||
return DOUBLE_QUOTE_MATCHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a matcher which matches the single or double quote character.
|
||||
*
|
||||
* @return a matcher for a single or double quote
|
||||
*/
|
||||
public static StrMatcher quoteMatcher() {
|
||||
return QUOTE_MATCHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches no characters.
|
||||
*
|
||||
* @return a matcher that matches nothing
|
||||
*/
|
||||
public static StrMatcher noneMatcher() {
|
||||
return NONE_MATCHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that creates a matcher from a character.
|
||||
*
|
||||
* @param ch the character to match, must not be null
|
||||
* @return a new Matcher for the given char
|
||||
*/
|
||||
public static StrMatcher charMatcher(final char ch) {
|
||||
return new CharMatcher(ch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that creates a matcher from a set of characters.
|
||||
*
|
||||
* @param chars the characters to match, null or empty matches nothing
|
||||
* @return a new matcher for the given char[]
|
||||
*/
|
||||
public static StrMatcher charSetMatcher(final char... chars) {
|
||||
if (chars == null || chars.length == 0) {
|
||||
return NONE_MATCHER;
|
||||
}
|
||||
if (chars.length == 1) {
|
||||
return new CharMatcher(chars[0]);
|
||||
}
|
||||
return new CharSetMatcher(chars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that creates a matcher from a string representing a set of
|
||||
* characters.
|
||||
*
|
||||
* @param chars the characters to match, null or empty matches nothing
|
||||
* @return a new Matcher for the given characters
|
||||
*/
|
||||
public static StrMatcher charSetMatcher(final String chars) {
|
||||
if (StringUtils.isEmpty(chars)) {
|
||||
return NONE_MATCHER;
|
||||
}
|
||||
if (chars.length() == 1) {
|
||||
return new CharMatcher(chars.charAt(0));
|
||||
}
|
||||
return new CharSetMatcher(chars.toCharArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that creates a matcher from a string.
|
||||
*
|
||||
* @param str the string to match, null or empty matches nothing
|
||||
* @return a new Matcher for the given String
|
||||
*/
|
||||
public static StrMatcher stringMatcher(final String str) {
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
return NONE_MATCHER;
|
||||
}
|
||||
return new StringMatcher(str);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
protected StrMatcher() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of matching characters, zero for no match.
|
||||
* <p>
|
||||
* This method is called to check for a match. The parameter {@code pos}
|
||||
* represents the current position to be checked in the string {@code buffer} (a
|
||||
* character array which must not be changed). The API guarantees that
|
||||
* {@code pos} is a valid index for {@code buffer}.
|
||||
* <p>
|
||||
* The character array may be larger than the active area to be matched. Only
|
||||
* values in the buffer between the specified indices may be accessed.
|
||||
* <p>
|
||||
* The matching code may check one character or many. It may check characters
|
||||
* preceding {@code pos} as well as those after, so long as no checks exceed the
|
||||
* bounds specified.
|
||||
* <p>
|
||||
* It must return zero for no match, or a positive number if a match was found.
|
||||
* The number indicates the number of characters that matched.
|
||||
*
|
||||
* @param buffer the text content to match against, do not change
|
||||
* @param pos the starting position for the match, valid for buffer
|
||||
* @param bufferStart the first active index in the buffer, valid for buffer
|
||||
* @param bufferEnd the end index (exclusive) of the active buffer, valid for
|
||||
* buffer
|
||||
* @return the number of matching characters, zero for no match
|
||||
*/
|
||||
public abstract int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd);
|
||||
|
||||
/**
|
||||
* Returns the number of matching characters, zero for no match.
|
||||
* <p>
|
||||
* This method is called to check for a match. The parameter {@code pos}
|
||||
* represents the current position to be checked in the string {@code buffer} (a
|
||||
* character array which must not be changed). The API guarantees that
|
||||
* {@code pos} is a valid index for {@code buffer}.
|
||||
* <p>
|
||||
* The matching code may check one character or many. It may check characters
|
||||
* preceding {@code pos} as well as those after.
|
||||
* <p>
|
||||
* It must return zero for no match, or a positive number if a match was found.
|
||||
* The number indicates the number of characters that matched.
|
||||
*
|
||||
* @param buffer the text content to match against, do not change
|
||||
* @param pos the starting position for the match, valid for buffer
|
||||
* @return the number of matching characters, zero for no match
|
||||
* @since 2.4
|
||||
*/
|
||||
public int isMatch(final char[] buffer, final int pos) {
|
||||
return isMatch(buffer, pos, 0, buffer.length);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
/**
|
||||
* Class used to define a set of characters for matching purposes.
|
||||
*/
|
||||
static final class CharSetMatcher extends StrMatcher {
|
||||
/** The set of characters to match. */
|
||||
private final char[] chars;
|
||||
|
||||
/**
|
||||
* Constructor that creates a matcher from a character array.
|
||||
*
|
||||
* @param chars the characters to match, must not be null
|
||||
*/
|
||||
CharSetMatcher(final char[] chars) {
|
||||
this.chars = ArraySorter.sort(chars.clone());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the given character matches.
|
||||
*
|
||||
* @param buffer the text content to match against, do not change
|
||||
* @param pos the starting position for the match, valid for buffer
|
||||
* @param bufferStart the first active index in the buffer, valid for buffer
|
||||
* @param bufferEnd the end index of the active buffer, valid for buffer
|
||||
* @return the number of matching characters, zero for no match
|
||||
*/
|
||||
@Override
|
||||
public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
|
||||
return Arrays.binarySearch(chars, buffer[pos]) >= 0 ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
/**
|
||||
* Class used to define a character for matching purposes.
|
||||
*/
|
||||
static final class CharMatcher extends StrMatcher {
|
||||
/** The character to match. */
|
||||
private final char ch;
|
||||
|
||||
/**
|
||||
* Constructor that creates a matcher that matches a single character.
|
||||
*
|
||||
* @param ch the character to match
|
||||
*/
|
||||
CharMatcher(final char ch) {
|
||||
this.ch = ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the given character matches.
|
||||
*
|
||||
* @param buffer the text content to match against, do not change
|
||||
* @param pos the starting position for the match, valid for buffer
|
||||
* @param bufferStart the first active index in the buffer, valid for buffer
|
||||
* @param bufferEnd the end index of the active buffer, valid for buffer
|
||||
* @return the number of matching characters, zero for no match
|
||||
*/
|
||||
@Override
|
||||
public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
|
||||
return ch == buffer[pos] ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
/**
|
||||
* Class used to define a set of characters for matching purposes.
|
||||
*/
|
||||
static final class StringMatcher extends StrMatcher {
|
||||
/** The string to match, as a character array. */
|
||||
private final char[] chars;
|
||||
|
||||
/**
|
||||
* Constructor that creates a matcher from a String.
|
||||
*
|
||||
* @param str the string to match, must not be null
|
||||
*/
|
||||
StringMatcher(final String str) {
|
||||
chars = str.toCharArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the given text matches the stored string.
|
||||
*
|
||||
* @param buffer the text content to match against, do not change
|
||||
* @param pos the starting position for the match, valid for buffer
|
||||
* @param bufferStart the first active index in the buffer, valid for buffer
|
||||
* @param bufferEnd the end index of the active buffer, valid for buffer
|
||||
* @return the number of matching characters, zero for no match
|
||||
*/
|
||||
@Override
|
||||
public int isMatch(final char[] buffer, int pos, final int bufferStart, final int bufferEnd) {
|
||||
final int len = chars.length;
|
||||
if (pos + len > bufferEnd) {
|
||||
return 0;
|
||||
}
|
||||
for (int i = 0; i < chars.length; i++, pos++) {
|
||||
if (chars[i] != buffer[pos]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + ' ' + Arrays.toString(chars);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
/**
|
||||
* Class used to match no characters.
|
||||
*/
|
||||
static final class NoMatcher extends StrMatcher {
|
||||
|
||||
/**
|
||||
* Constructs a new instance of {@code NoMatcher}.
|
||||
*/
|
||||
NoMatcher() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Always returns {@code false}.
|
||||
*
|
||||
* @param buffer the text content to match against, do not change
|
||||
* @param pos the starting position for the match, valid for buffer
|
||||
* @param bufferStart the first active index in the buffer, valid for buffer
|
||||
* @param bufferEnd the end index of the active buffer, valid for buffer
|
||||
* @return the number of matching characters, zero for no match
|
||||
*/
|
||||
@Override
|
||||
public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
/**
|
||||
* Class used to match whitespace as per trim().
|
||||
*/
|
||||
static final class TrimMatcher extends StrMatcher {
|
||||
|
||||
/**
|
||||
* Constructs a new instance of {@code TrimMatcher}.
|
||||
*/
|
||||
TrimMatcher() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the given character matches.
|
||||
*
|
||||
* @param buffer the text content to match against, do not change
|
||||
* @param pos the starting position for the match, valid for buffer
|
||||
* @param bufferStart the first active index in the buffer, valid for buffer
|
||||
* @param bufferEnd the end index of the active buffer, valid for buffer
|
||||
* @return the number of matching characters, zero for no match
|
||||
*/
|
||||
@Override
|
||||
public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
|
||||
return buffer[pos] <= 32 ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,6 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.bungeeprotocol.EaglerBungeeProtocol;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.protocol.Protocol;
|
||||
|
||||
@ -26,11 +25,11 @@ public class EaglerChannelWrapper extends ChannelWrapper {
|
||||
super(ctx);
|
||||
}
|
||||
|
||||
public void setProtocol(EaglerBungeeProtocol protocol) {
|
||||
public void setProtocol(Protocol protocol) {
|
||||
getHandle().pipeline().get(EaglerMinecraftEncoder.class).setProtocol(protocol);
|
||||
getHandle().pipeline().get(EaglerMinecraftDecoder.class).setProtocol(protocol);
|
||||
}
|
||||
|
||||
|
||||
public void setVersion(int protocol) {
|
||||
getHandle().pipeline().get(EaglerMinecraftEncoder.class).setProtocolVersion(protocol);
|
||||
getHandle().pipeline().get(EaglerMinecraftDecoder.class).setProtocolVersion(protocol);
|
||||
@ -41,24 +40,12 @@ public class EaglerChannelWrapper extends ChannelWrapper {
|
||||
public Protocol getEncodeProtocol() {
|
||||
EaglerMinecraftEncoder enc;
|
||||
if (this.getHandle() == null || (enc = this.getHandle().pipeline().get(EaglerMinecraftEncoder.class)) == null) return lastProtocol;
|
||||
EaglerBungeeProtocol eaglerProtocol = enc.getProtocol();
|
||||
switch(eaglerProtocol) {
|
||||
case GAME:
|
||||
return (lastProtocol = Protocol.GAME);
|
||||
case HANDSHAKE:
|
||||
return (lastProtocol = Protocol.HANDSHAKE);
|
||||
case LOGIN:
|
||||
return (lastProtocol = Protocol.LOGIN);
|
||||
case STATUS:
|
||||
return (lastProtocol = Protocol.STATUS);
|
||||
default:
|
||||
return lastProtocol;
|
||||
}
|
||||
return (lastProtocol = enc.getProtocol());
|
||||
}
|
||||
|
||||
|
||||
public void close(Object o) {
|
||||
super.close(o);
|
||||
EaglerPipeline.closeChannel(getHandle());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
|
||||
/**
|
||||
@ -33,11 +34,12 @@ public class EaglerConnectionInstance {
|
||||
public boolean isRegularHttp = false;
|
||||
|
||||
public UserConnection userConnection = null;
|
||||
public HttpServerQueryHandler queryHandler = null;
|
||||
|
||||
public EaglerConnectionInstance(Channel channel) {
|
||||
this.channel = channel;
|
||||
this.creationTime = this.lastServerPingPacket = this.lastClientPingPacket =
|
||||
this.lastClientPongPacket = System.currentTimeMillis();
|
||||
this.lastClientPongPacket = EaglerXBungeeAPIHelper.steadyTimeMillis();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,24 +1,56 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import com.google.common.collect.Collections2;
|
||||
|
||||
import gnu.trove.set.TIntSet;
|
||||
import gnu.trove.set.hash.TIntHashSet;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EnumWebViewState;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.NotificationBadgeBuilder;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftVoiceStatusChangeEvent;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerBungeeConfig;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.backend_rpc_protocol.BackendRPCSessionHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.backend_rpc_protocol.EnumSubscribedEvent;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.protocol.GameProtocolMessageController;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SimpleRateLimiter;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketCustomizePauseMenuV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeHideV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeShowV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifIconsRegisterV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifIconsReleaseV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketRedirectClientV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketSetServerCookieV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketWebViewMessageV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.PacketImageData;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.connection.Server;
|
||||
import net.md_5.bungee.connection.InitialHandler;
|
||||
import net.md_5.bungee.connection.LoginResult;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.PacketWrapper;
|
||||
import net.md_5.bungee.protocol.Property;
|
||||
@ -33,7 +65,7 @@ import net.md_5.bungee.protocol.packet.PluginMessage;
|
||||
import net.md_5.bungee.protocol.packet.StatusRequest;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2023 lax1dude. All Rights Reserved.
|
||||
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
@ -58,47 +90,84 @@ public class EaglerInitialHandler extends InitialHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private final int gameProtocolVersion;
|
||||
private final String username;
|
||||
private final UUID playerUUID;
|
||||
private final UUID playerUUIDOffline;
|
||||
private final UUID playerUUIDRewrite;
|
||||
private LoginResult loginResult;
|
||||
private final InetSocketAddress eaglerAddress;
|
||||
private final InetSocketAddress virtualHost;
|
||||
private final Unsafe eaglerUnsafe;
|
||||
protected final int clientProtocolVersion;
|
||||
protected final int gameProtocolVersion;
|
||||
protected final String clientBrandString;
|
||||
protected final String clientVersionString;
|
||||
protected final UUID clientBrandUUID;
|
||||
protected final String username;
|
||||
protected final UUID playerUUID;
|
||||
protected final UUID playerUUIDOffline;
|
||||
protected final UUID playerUUIDRewrite;
|
||||
protected final InetSocketAddress eaglerAddress;
|
||||
protected final InetSocketAddress virtualHost;
|
||||
protected final Unsafe eaglerUnsafe;
|
||||
public final SimpleRateLimiter skinLookupRateLimiter;
|
||||
public final SimpleRateLimiter skinUUIDLookupRateLimiter;
|
||||
public final SimpleRateLimiter skinTextureDownloadRateLimiter;
|
||||
public final SimpleRateLimiter capeLookupRateLimiter;
|
||||
public final SimpleRateLimiter voiceConnectRateLimiter;
|
||||
public final String origin;
|
||||
protected final String origin;
|
||||
protected final String userAgent;
|
||||
public final ClientCertificateHolder clientCertificate;
|
||||
public final Set<ClientCertificateHolder> certificatesToSend;
|
||||
public final TIntSet certificatesSent;
|
||||
public boolean currentFNAWSkinEnableStatus = true;
|
||||
public final EaglerChannelWrapper ch;
|
||||
public final AtomicBoolean currentFNAWSkinEnableStatus = new AtomicBoolean(true);
|
||||
public final AtomicBoolean currentFNAWSkinForceStatus = new AtomicBoolean(false);
|
||||
volatile GameProtocolMessageController messageProtocolController = null;
|
||||
protected final boolean allowCookie;
|
||||
protected volatile byte[] cookie;
|
||||
public volatile SkinPacketVersionCache originalSkin = null;
|
||||
public volatile GameMessagePacket originalCape = null;
|
||||
protected final Map<String,byte[]> otherProfileDataFromHanshake;
|
||||
public boolean isWebViewChannelAllowed = false;
|
||||
public final AtomicBoolean webViewMessageChannelOpen = new AtomicBoolean(false);
|
||||
public volatile String webViewMessageChannelName = null;
|
||||
public final AtomicBoolean hasSentServerInfo = new AtomicBoolean(false);
|
||||
public final List<GameMessagePacket> serverInfoSendBuffer = new LinkedList<>();
|
||||
protected BackendRPCSessionHandler backedRPCSessionHandler = null;
|
||||
protected final AtomicReference<EaglercraftVoiceStatusChangeEvent.EnumVoiceState> lastVoiceState = new AtomicReference<>(
|
||||
EaglercraftVoiceStatusChangeEvent.EnumVoiceState.SERVER_DISABLE);
|
||||
|
||||
private static final Property[] NO_PROPERTIES = new Property[0];
|
||||
|
||||
public EaglerInitialHandler(BungeeCord bungee, EaglerListenerConfig listener, final ChannelWrapper ch,
|
||||
int gameProtocolVersion, String username, UUID playerUUID, UUID offlineUUID, InetSocketAddress address,
|
||||
String host, String origin, ClientCertificateHolder clientCertificate) {
|
||||
public EaglerInitialHandler(BungeeCord bungee, EaglerListenerConfig listener, final EaglerChannelWrapper ch,
|
||||
int clientProtocolVersion, int gameProtocolVersion, String clientBrandString, String clientVersionString,
|
||||
UUID clientBrandUUID, String username, UUID playerUUID, UUID offlineUUID, InetSocketAddress address,
|
||||
String host, String origin, String userAgent, ClientCertificateHolder clientCertificate,
|
||||
boolean allowCookie, byte[] cookie, Map<String,byte[]> otherProfileData) {
|
||||
super(bungee, listener);
|
||||
this.ch = ch;
|
||||
this.clientProtocolVersion = clientProtocolVersion;
|
||||
this.gameProtocolVersion = gameProtocolVersion;
|
||||
this.clientBrandString = clientBrandString;
|
||||
this.clientVersionString = clientVersionString;
|
||||
this.clientBrandUUID = clientBrandUUID;
|
||||
this.username = username;
|
||||
this.playerUUID = playerUUID;
|
||||
this.playerUUIDOffline = offlineUUID;
|
||||
this.playerUUIDRewrite = bungee.config.isIpForward() ? playerUUID : offlineUUID;
|
||||
this.eaglerAddress = address;
|
||||
this.origin = origin;
|
||||
this.userAgent = userAgent;
|
||||
this.skinLookupRateLimiter = new SimpleRateLimiter();
|
||||
this.skinUUIDLookupRateLimiter = new SimpleRateLimiter();
|
||||
this.skinTextureDownloadRateLimiter = new SimpleRateLimiter();
|
||||
this.capeLookupRateLimiter = new SimpleRateLimiter();
|
||||
this.voiceConnectRateLimiter = new SimpleRateLimiter();
|
||||
this.allowCookie = allowCookie;
|
||||
this.cookie = cookie;
|
||||
this.otherProfileDataFromHanshake = otherProfileData;
|
||||
this.clientCertificate = clientCertificate;
|
||||
this.certificatesToSend = new HashSet();
|
||||
this.certificatesToSend = new HashSet<>();
|
||||
this.certificatesSent = new TIntHashSet();
|
||||
EaglerBungeeConfig conf = EaglerXBungee.getEagler().getConfig();
|
||||
SPacketCustomizePauseMenuV4EAG pkt = conf.getPauseMenuConf().getPacket();
|
||||
this.isWebViewChannelAllowed = pkt != null
|
||||
&& (pkt.serverInfoEmbedPerms & SPacketCustomizePauseMenuV4EAG.SERVER_INFO_EMBED_PERMS_MESSAGE_API) != 0;
|
||||
this.backedRPCSessionHandler = conf.getEnableBackendRPCAPI()
|
||||
? BackendRPCSessionHandler.createForPlayer(this) : null;
|
||||
if(clientCertificate != null) {
|
||||
this.certificatesSent.add(clientCertificate.hashCode());
|
||||
}
|
||||
@ -131,6 +200,255 @@ public class EaglerInitialHandler extends InitialHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public GameProtocolMessageController getEaglerMessageController() {
|
||||
return messageProtocolController;
|
||||
}
|
||||
|
||||
public GamePluginMessageProtocol getEaglerProtocol() {
|
||||
return messageProtocolController == null ? GamePluginMessageProtocol.getByVersion(clientProtocolVersion)
|
||||
: messageProtocolController.protocol;
|
||||
}
|
||||
|
||||
public int getEaglerProtocolHandshake() {
|
||||
return clientProtocolVersion;
|
||||
}
|
||||
|
||||
public void sendEaglerMessage(GameMessagePacket pkt) {
|
||||
if(messageProtocolController != null) {
|
||||
try {
|
||||
messageProtocolController.sendPacket(pkt);
|
||||
} catch (IOException e) {
|
||||
this.disconnect(new TextComponent("Failed to write eaglercraft packet! (" + e.toString() + ")"));
|
||||
}
|
||||
}else {
|
||||
throw new IllegalStateException("Race condition detected, messageProtocolController is null! (wait until getEaglerMessageController() does not return null before sending the packet)");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getWebViewSupport() {
|
||||
return getEaglerProtocol().ver >= 4;
|
||||
}
|
||||
|
||||
public void setWebViewChannelAllowed(boolean en) {
|
||||
isWebViewChannelAllowed = en;
|
||||
}
|
||||
public boolean getWebViewChannelAllowed() {
|
||||
return isWebViewChannelAllowed;
|
||||
}
|
||||
|
||||
public boolean getWebViewMessageChannelOpen() {
|
||||
return webViewMessageChannelOpen.get();
|
||||
}
|
||||
|
||||
public String getWebViewMessageChannelName() {
|
||||
return webViewMessageChannelName;
|
||||
}
|
||||
|
||||
public void sendWebViewMessage(int type, byte[] bytes) {
|
||||
if(webViewMessageChannelOpen.get()) {
|
||||
sendEaglerMessage(new SPacketWebViewMessageV4EAG(type, bytes));
|
||||
}else {
|
||||
EaglerXBungee.logger().warning("[" + getSocketAddress().toString() + "]: Some plugin tried to send a webview message to player \"" + username + "\", but the player doesn't have a webview message channel open!");
|
||||
}
|
||||
}
|
||||
public void sendWebViewMessage(String str) {
|
||||
if(webViewMessageChannelOpen.get()) {
|
||||
sendEaglerMessage(new SPacketWebViewMessageV4EAG(str));
|
||||
}else {
|
||||
EaglerXBungee.logger().warning("[" + getSocketAddress().toString() + "]: Some plugin tried to send a webview message to player \"" + username + "\", but the player doesn't have a webview message channel open!");
|
||||
}
|
||||
}
|
||||
|
||||
public void sendWebViewMessage(byte[] bin) {
|
||||
if(webViewMessageChannelOpen.get()) {
|
||||
sendEaglerMessage(new SPacketWebViewMessageV4EAG(bin));
|
||||
}else {
|
||||
EaglerXBungee.logger().warning("[" + getSocketAddress().toString() + "]: Some plugin tried to send a webview message to player \"" + username + "\", but the player doesn't have a webview message channel open!");
|
||||
}
|
||||
}
|
||||
|
||||
public EnumWebViewState getWebViewState() {
|
||||
if(!getWebViewSupport()) {
|
||||
return EnumWebViewState.NOT_SUPPORTED;
|
||||
}
|
||||
if(isWebViewChannelAllowed) {
|
||||
if(webViewMessageChannelOpen.get()) {
|
||||
return EnumWebViewState.CHANNEL_OPEN;
|
||||
}else {
|
||||
return EnumWebViewState.CHANNEL_CLOSED;
|
||||
}
|
||||
}else {
|
||||
return EnumWebViewState.SERVER_DISABLE;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getCookieAllowed() {
|
||||
return allowCookie;
|
||||
}
|
||||
|
||||
public byte[] getCookieData() {
|
||||
return allowCookie ? cookie : null;
|
||||
}
|
||||
|
||||
public void setCookieData(byte[] data, long expiresAfter, TimeUnit timeUnit) {
|
||||
setCookieData(data, timeUnit.toSeconds(expiresAfter), false, true);
|
||||
}
|
||||
|
||||
public void setCookieData(byte[] data, long expiresAfter, TimeUnit timeUnit, boolean revokeQuerySupported) {
|
||||
setCookieData(data, timeUnit.toSeconds(expiresAfter), revokeQuerySupported, true);
|
||||
}
|
||||
|
||||
public void setCookieData(byte[] data, long expiresAfter, TimeUnit timeUnit, boolean revokeQuerySupported, boolean clientSaveCookieToDisk) {
|
||||
setCookieData(data, timeUnit.toSeconds(expiresAfter), revokeQuerySupported, clientSaveCookieToDisk);
|
||||
}
|
||||
|
||||
public void setCookieData(byte[] data, long expiresAfterSec, boolean revokeQuerySupported, boolean clientSaveCookieToDisk) {
|
||||
if(allowCookie) {
|
||||
if(expiresAfterSec < 0l) {
|
||||
expiresAfterSec = 0l;
|
||||
data = null;
|
||||
}
|
||||
if(data == null) {
|
||||
cookie = null;
|
||||
sendEaglerMessage(new SPacketSetServerCookieV4EAG(null, 01, false, false));
|
||||
return;
|
||||
}
|
||||
if(data.length > 255) {
|
||||
throw new IllegalArgumentException("Cookie cannot be longer than 255 bytes!");
|
||||
}
|
||||
if(expiresAfterSec > 604800l) {
|
||||
throw new IllegalArgumentException("Cookie cannot be set for longer than 7 days! (tried " + (expiresAfterSec / 604800l) + " days)");
|
||||
}
|
||||
cookie = data;
|
||||
sendEaglerMessage(new SPacketSetServerCookieV4EAG(data, expiresAfterSec, revokeQuerySupported, clientSaveCookieToDisk));
|
||||
}else {
|
||||
EaglerXBungee.logger().warning("[" + getSocketAddress().toString() + "]: Some plugin tried to set a cookie for player \"" + username + "\", but the player has cookies disabled!");
|
||||
}
|
||||
}
|
||||
|
||||
public void clearCookieData() {
|
||||
setCookieData(null, 0, false, false);
|
||||
}
|
||||
|
||||
public boolean notificationSupported() {
|
||||
return clientProtocolVersion >= 4;
|
||||
}
|
||||
|
||||
public void registerNotificationIcon(UUID uuid, PacketImageData imageData) {
|
||||
if(clientProtocolVersion >= 4) {
|
||||
sendEaglerMessage(new SPacketNotifIconsRegisterV4EAG(
|
||||
Arrays.asList(new SPacketNotifIconsRegisterV4EAG.CreateIcon(uuid.getMostSignificantBits(),
|
||||
uuid.getLeastSignificantBits(), imageData))));
|
||||
}else {
|
||||
EaglerXBungee.logger().warning("[" + getSocketAddress().toString() + "]: Some plugin tried to register notification icons for player \"" + username + "\", but the player has notifications disabled!");
|
||||
}
|
||||
}
|
||||
|
||||
public void registerNotificationIcons(Map<UUID,PacketImageData> imageDatas) {
|
||||
if(clientProtocolVersion >= 4) {
|
||||
sendEaglerMessage(new SPacketNotifIconsRegisterV4EAG(
|
||||
new ArrayList<>(Collections2.transform(imageDatas.entrySet(), (etr) -> {
|
||||
UUID key = etr.getKey();
|
||||
return new SPacketNotifIconsRegisterV4EAG.CreateIcon(key.getMostSignificantBits(),
|
||||
key.getLeastSignificantBits(), etr.getValue());
|
||||
}))));
|
||||
}else {
|
||||
EaglerXBungee.logger().warning("[" + getSocketAddress().toString() + "]: Some plugin tried to register notification icons for player \"" + username + "\", but the player has notifications disabled!");
|
||||
}
|
||||
}
|
||||
|
||||
public void showNotificationBadge(NotificationBadgeBuilder badgeBuilder) {
|
||||
if(clientProtocolVersion >= 4) {
|
||||
sendEaglerMessage(badgeBuilder.buildPacket());
|
||||
}else {
|
||||
EaglerXBungee.logger().warning("[" + getSocketAddress().toString() + "]: Some plugin tried to show notification badges to player \"" + username + "\", but the player has notifications disabled!");
|
||||
}
|
||||
}
|
||||
|
||||
public void showNotificationBadge(SPacketNotifBadgeShowV4EAG badgePacket) {
|
||||
if(clientProtocolVersion >= 4) {
|
||||
sendEaglerMessage(badgePacket);
|
||||
}else {
|
||||
EaglerXBungee.logger().warning("[" + getSocketAddress().toString() + "]: Some plugin tried to show notification badges to player \"" + username + "\", but the player has notifications disabled!");
|
||||
}
|
||||
}
|
||||
|
||||
public void hideNotificationBadge(UUID badgeUUID) {
|
||||
if(clientProtocolVersion >= 4) {
|
||||
sendEaglerMessage(new SPacketNotifBadgeHideV4EAG(badgeUUID.getMostSignificantBits(), badgeUUID.getLeastSignificantBits()));
|
||||
}else {
|
||||
EaglerXBungee.logger().warning("[" + getSocketAddress().toString() + "]: Some plugin tried to hide notification badges for player \"" + username + "\", but the player has notifications disabled!");
|
||||
}
|
||||
}
|
||||
|
||||
public void releaseNotificationIcon(UUID uuid) {
|
||||
if(clientProtocolVersion >= 4) {
|
||||
sendEaglerMessage(new SPacketNotifIconsReleaseV4EAG(
|
||||
Arrays.asList(new SPacketNotifIconsReleaseV4EAG.DestroyIcon(uuid.getMostSignificantBits(),
|
||||
uuid.getLeastSignificantBits()))));
|
||||
}else {
|
||||
EaglerXBungee.logger().warning("[" + getSocketAddress().toString() + "]: Some plugin tried to release notification icons for player \"" + username + "\", but the player has notifications disabled!");
|
||||
}
|
||||
}
|
||||
|
||||
public void releaseNotificationIcons(Collection<UUID> uuids) {
|
||||
if(clientProtocolVersion >= 4) {
|
||||
sendEaglerMessage(new SPacketNotifIconsReleaseV4EAG(new ArrayList<>(Collections2.transform(uuids,
|
||||
(etr) -> new SPacketNotifIconsReleaseV4EAG.DestroyIcon(etr.getMostSignificantBits(),
|
||||
etr.getLeastSignificantBits())))));
|
||||
}else {
|
||||
EaglerXBungee.logger().warning("[" + getSocketAddress().toString() + "]: Some plugin tried to release notification icons for player \"" + username + "\", but the player has notifications disabled!");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean redirectToWebSocketSupported() {
|
||||
return clientProtocolVersion >= 4;
|
||||
}
|
||||
|
||||
public void redirectPlayerToWebSocket(String serverAddress) {
|
||||
if(getEaglerProtocol().ver >= 4) {
|
||||
sendEaglerMessage(new SPacketRedirectClientV4EAG(serverAddress));
|
||||
}else {
|
||||
EaglerXBungee.logger().warning("[" + getSocketAddress().toString() + "]: Some plugin tried to redirect player \"" + username + "\" to a different websocket, but that player's client doesn't support this feature!");
|
||||
}
|
||||
}
|
||||
|
||||
public BackendRPCSessionHandler getRPCSessionHandler() {
|
||||
return backedRPCSessionHandler;
|
||||
}
|
||||
|
||||
public boolean getRPCEventSubscribed(EnumSubscribedEvent event) {
|
||||
return backedRPCSessionHandler != null && backedRPCSessionHandler.isSubscribed(event);
|
||||
}
|
||||
|
||||
public void handleBackendRPCPacket(Server server, byte[] data) {
|
||||
if(backedRPCSessionHandler != null) {
|
||||
backedRPCSessionHandler.handleRPCPacket(server, data);
|
||||
}else {
|
||||
EaglerXBungee.logger().severe("[" + getSocketAddress().toString() + "]: Server tried to send backend RPC packet to player \"" + username + "\" but this feature is not enabled. Enable it by setting \"enable_backend_rpc_api: true\" in settings.yml");
|
||||
}
|
||||
}
|
||||
|
||||
public void fireVoiceStateChange(EaglercraftVoiceStatusChangeEvent.EnumVoiceState state) {
|
||||
EaglercraftVoiceStatusChangeEvent.EnumVoiceState oldState = lastVoiceState.getAndSet(state);
|
||||
if(state != oldState) {
|
||||
BungeeCord.getInstance().getPluginManager().callEvent(new EaglercraftVoiceStatusChangeEvent(
|
||||
EaglerXBungeeAPIHelper.getPlayer(this), getEaglerListenerConfig(), this, oldState, state));
|
||||
}
|
||||
}
|
||||
|
||||
public String getEaglerBrandString() {
|
||||
return clientBrandString;
|
||||
}
|
||||
|
||||
public String getEaglerVersionString() {
|
||||
return clientVersionString;
|
||||
}
|
||||
|
||||
public UUID getClientBrandUUID() {
|
||||
return clientBrandUUID;
|
||||
}
|
||||
|
||||
public static UUID generateOfflineUUID(byte[] username) {
|
||||
String offlinePlayerStr = "OfflinePlayer:";
|
||||
byte[] uuidHashGenerator = new byte[offlinePlayerStr.length() + username.length];
|
||||
@ -139,16 +457,29 @@ public class EaglerInitialHandler extends InitialHandler {
|
||||
return UUID.nameUUIDFromBytes(uuidHashGenerator);
|
||||
}
|
||||
|
||||
void setLoginProfile(LoginResult obj) {
|
||||
this.loginResult = obj;
|
||||
private static final Field loginProfileField;
|
||||
|
||||
static {
|
||||
try {
|
||||
Field f = InitialHandler.class.getDeclaredField("loginProfile");
|
||||
f.setAccessible(true);
|
||||
f.set(this, obj);
|
||||
loginProfileField = InitialHandler.class.getDeclaredField("loginProfile");
|
||||
loginProfileField.setAccessible(true);
|
||||
}catch(Throwable t) {
|
||||
throw new RuntimeException("Could not access loginProfile field", t);
|
||||
}
|
||||
}
|
||||
|
||||
void setLoginProfile(LoginResult obj) {
|
||||
try {
|
||||
loginProfileField.set(this, obj);
|
||||
}catch(Throwable t) {
|
||||
throw new RuntimeException("Could not perform reflection", t);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getOtherProfileDataFromHandshake(String name) {
|
||||
return otherProfileDataFromHanshake.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketWrapper packet) throws Exception {
|
||||
}
|
||||
@ -266,11 +597,6 @@ public class EaglerInitialHandler extends InitialHandler {
|
||||
return playerUUID.toString().replace("-", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginResult getLoginProfile() {
|
||||
return loginResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getVirtualHost() {
|
||||
return virtualHost;
|
||||
@ -290,7 +616,12 @@ public class EaglerInitialHandler extends InitialHandler {
|
||||
return origin;
|
||||
}
|
||||
|
||||
public String getUserAgent() {
|
||||
return userAgent;
|
||||
}
|
||||
|
||||
public EaglerListenerConfig getEaglerListenerConfig() {
|
||||
return (EaglerListenerConfig)getListener();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server;
|
||||
import java.util.List;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
|
||||
|
@ -11,10 +11,8 @@ import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
|
||||
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
|
||||
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.bungeeprotocol.EaglerBungeeProtocol;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.bungeeprotocol.EaglerProtocolAccessProxy;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.PacketWrapper;
|
||||
import net.md_5.bungee.protocol.Protocol;
|
||||
@ -36,7 +34,7 @@ import net.md_5.bungee.protocol.ProtocolConstants.Direction;
|
||||
*
|
||||
*/
|
||||
public class EaglerMinecraftDecoder extends MessageToMessageDecoder<WebSocketFrame> {
|
||||
private EaglerBungeeProtocol protocol;
|
||||
private Protocol protocol;
|
||||
private final boolean server;
|
||||
private int protocolVersion;
|
||||
private static Constructor<PacketWrapper> packetWrapperConstructor = null;
|
||||
@ -47,27 +45,13 @@ public class EaglerMinecraftDecoder extends MessageToMessageDecoder<WebSocketFra
|
||||
return;
|
||||
}
|
||||
EaglerConnectionInstance con = ctx.channel().attr(EaglerPipeline.CONNECTION_INSTANCE).get();
|
||||
long millis = System.currentTimeMillis();
|
||||
long millis = EaglerXBungeeAPIHelper.steadyTimeMillis();
|
||||
if(frame instanceof BinaryWebSocketFrame) {
|
||||
BinaryWebSocketFrame in = (BinaryWebSocketFrame) frame;
|
||||
ByteBuf buf = in.content();
|
||||
buf.markReaderIndex();
|
||||
int pktId = DefinedPacket.readVarInt(buf);
|
||||
DefinedPacket pkt = EaglerProtocolAccessProxy.createPacket(protocol, protocolVersion, pktId, server);
|
||||
Protocol bungeeProtocol = null;
|
||||
switch(this.protocol) {
|
||||
case GAME:
|
||||
bungeeProtocol = Protocol.GAME;
|
||||
break;
|
||||
case HANDSHAKE:
|
||||
bungeeProtocol = Protocol.HANDSHAKE;
|
||||
break;
|
||||
case LOGIN:
|
||||
bungeeProtocol = Protocol.LOGIN;
|
||||
break;
|
||||
case STATUS:
|
||||
bungeeProtocol = Protocol.STATUS;
|
||||
}
|
||||
if(pkt != null) {
|
||||
pkt.read(buf, server ? Direction.TO_CLIENT : Direction.TO_SERVER, protocolVersion);
|
||||
if(buf.isReadable()) {
|
||||
@ -75,11 +59,11 @@ public class EaglerMinecraftDecoder extends MessageToMessageDecoder<WebSocketFra
|
||||
pkt.getClass().getSimpleName() + " had extra bytes! (" + buf.readableBytes() + ")");
|
||||
}else {
|
||||
buf.resetReaderIndex();
|
||||
out.add(this.wrapPacket(pkt, buf, bungeeProtocol));
|
||||
out.add(this.wrapPacket(pkt, buf, protocol));
|
||||
}
|
||||
}else {
|
||||
buf.resetReaderIndex();
|
||||
out.add(this.wrapPacket(null, buf, bungeeProtocol));
|
||||
out.add(this.wrapPacket(null, buf, protocol));
|
||||
}
|
||||
}else if(frame instanceof PingWebSocketFrame) {
|
||||
if(millis - con.lastClientPingPacket > 500l) {
|
||||
@ -93,17 +77,17 @@ public class EaglerMinecraftDecoder extends MessageToMessageDecoder<WebSocketFra
|
||||
}
|
||||
}
|
||||
|
||||
public EaglerMinecraftDecoder(final EaglerBungeeProtocol protocol, final boolean server, final int protocolVersion) {
|
||||
public EaglerMinecraftDecoder(Protocol protocol, boolean server, int protocolVersion) {
|
||||
this.protocol = protocol;
|
||||
this.server = server;
|
||||
this.protocolVersion = protocolVersion;
|
||||
}
|
||||
|
||||
public void setProtocol(final EaglerBungeeProtocol protocol) {
|
||||
public void setProtocol(Protocol protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public void setProtocolVersion(final int protocolVersion) {
|
||||
public void setProtocolVersion(int protocolVersion) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,6 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.bungeeprotocol.EaglerBungeeProtocol;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.bungeeprotocol.EaglerProtocolAccessProxy;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.Protocol;
|
||||
import net.md_5.bungee.protocol.ProtocolConstants.Direction;
|
||||
@ -30,32 +28,18 @@ import net.md_5.bungee.protocol.ProtocolConstants.Direction;
|
||||
*/
|
||||
public class EaglerMinecraftEncoder extends MessageToMessageEncoder<DefinedPacket> {
|
||||
|
||||
private EaglerBungeeProtocol protocol;
|
||||
private Protocol protocol;
|
||||
private boolean server;
|
||||
private int protocolVersion;
|
||||
private static Method meth = null;
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, DefinedPacket msg, List<Object> out) throws Exception {
|
||||
Protocol bungeeProtocol = null;
|
||||
switch(this.protocol) {
|
||||
case GAME:
|
||||
bungeeProtocol = Protocol.GAME;
|
||||
break;
|
||||
case HANDSHAKE:
|
||||
bungeeProtocol = Protocol.HANDSHAKE;
|
||||
break;
|
||||
case LOGIN:
|
||||
bungeeProtocol = Protocol.LOGIN;
|
||||
break;
|
||||
case STATUS:
|
||||
bungeeProtocol = Protocol.STATUS;
|
||||
}
|
||||
ByteBuf buf = ctx.alloc().buffer();
|
||||
int pk = EaglerProtocolAccessProxy.getPacketId(protocol, protocolVersion, msg, server);
|
||||
DefinedPacket.writeVarInt(pk, buf);
|
||||
try {
|
||||
msg.write(buf, bungeeProtocol, server ? Direction.TO_CLIENT : Direction.TO_SERVER, protocolVersion);
|
||||
msg.write(buf, protocol, server ? Direction.TO_CLIENT : Direction.TO_SERVER, protocolVersion);
|
||||
} catch (NoSuchMethodError e) {
|
||||
try {
|
||||
if (meth == null) {
|
||||
@ -73,21 +57,21 @@ public class EaglerMinecraftEncoder extends MessageToMessageEncoder<DefinedPacke
|
||||
out.add(new BinaryWebSocketFrame(buf));
|
||||
}
|
||||
|
||||
public EaglerMinecraftEncoder(final EaglerBungeeProtocol protocol, final boolean server, final int protocolVersion) {
|
||||
public EaglerMinecraftEncoder(Protocol protocol, boolean server, int protocolVersion) {
|
||||
this.protocol = protocol;
|
||||
this.server = server;
|
||||
this.protocolVersion = protocolVersion;
|
||||
}
|
||||
|
||||
public void setProtocol(final EaglerBungeeProtocol protocol) {
|
||||
public void setProtocol(Protocol protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public void setProtocolVersion(final int protocolVersion) {
|
||||
public void setProtocolVersion(int protocolVersion) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
}
|
||||
|
||||
public EaglerBungeeProtocol getProtocol() {
|
||||
public Protocol getProtocol() {
|
||||
return this.protocol;
|
||||
}
|
||||
|
||||
|
@ -4,12 +4,18 @@ import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelException;
|
||||
@ -25,11 +31,21 @@ import io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtensio
|
||||
import io.netty.handler.codec.http.websocketx.extensions.compression.DeflateFrameServerExtensionHandshaker;
|
||||
import io.netty.handler.codec.http.websocketx.extensions.compression.PerMessageDeflateServerExtensionHandshaker;
|
||||
import io.netty.util.AttributeKey;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.EaglerBackendRPCProtocol;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerBungeeConfig;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler.ClientCertificateHolder;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.web.HttpWebServer;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketEnableFNAWSkinsEAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketUpdateCertEAG;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.ServerConnection;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.scheduler.BungeeScheduler;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
|
||||
@ -54,11 +70,13 @@ public class EaglerPipeline {
|
||||
public static final AttributeKey<InetAddress> REAL_ADDRESS = AttributeKey.valueOf("RealAddress");
|
||||
public static final AttributeKey<String> HOST = AttributeKey.valueOf("Host");
|
||||
public static final AttributeKey<String> ORIGIN = AttributeKey.valueOf("Origin");
|
||||
public static final AttributeKey<String> USER_AGENT = AttributeKey.valueOf("UserAgent");
|
||||
public static final int LOW_MARK = Integer.getInteger("net.md_5.bungee.low_mark", 524288);
|
||||
public static final int HIGH_MARK = Integer.getInteger("net.md_5.bungee.high_mark", 2097152);
|
||||
public static final WriteBufferWaterMark MARK = new WriteBufferWaterMark(LOW_MARK, HIGH_MARK);
|
||||
|
||||
public static final Collection<Channel> openChannels = new LinkedList();
|
||||
public static final Collection<Channel> openChannels = new LinkedList<>();
|
||||
public static final Set<UserConnection> waitingServerConnections = new HashSet<>();
|
||||
|
||||
public static final String UPDATE_CERT_CHANNEL = "EAG|UpdateCert-1.8";
|
||||
|
||||
@ -74,7 +92,7 @@ public class EaglerPipeline {
|
||||
long httpTimeout = conf.getBuiltinHttpServerTimeout();
|
||||
List<Channel> channelsList;
|
||||
synchronized(openChannels) {
|
||||
long millis = System.currentTimeMillis();
|
||||
long millis = EaglerXBungeeAPIHelper.steadyTimeMillis();
|
||||
Iterator<Channel> channelIterator = openChannels.iterator();
|
||||
while(channelIterator.hasNext()) {
|
||||
Channel c = channelIterator.next();
|
||||
@ -82,7 +100,15 @@ public class EaglerPipeline {
|
||||
long handshakeTimeoutForConnection = 500l;
|
||||
if(i.isRegularHttp) handshakeTimeoutForConnection = httpTimeout;
|
||||
else if(i.isWebSocket) handshakeTimeoutForConnection = handshakeTimeout;
|
||||
if(i == null || (!i.hasBeenForwarded && millis - i.creationTime > handshakeTimeoutForConnection)
|
||||
boolean hasTimeout = !i.hasBeenForwarded;
|
||||
if(i.queryHandler != null) {
|
||||
long l = i.queryHandler.getMaxAge();
|
||||
hasTimeout = l != -1l;
|
||||
if(hasTimeout) {
|
||||
handshakeTimeoutForConnection = l;
|
||||
}
|
||||
}
|
||||
if((hasTimeout && millis - i.creationTime > handshakeTimeoutForConnection)
|
||||
|| millis - i.lastClientPongPacket > keepAliveTimeout || !c.isActive()) {
|
||||
if(c.isActive()) {
|
||||
c.close();
|
||||
@ -102,7 +128,105 @@ public class EaglerPipeline {
|
||||
}
|
||||
}
|
||||
}
|
||||
channelsList = new ArrayList(openChannels);
|
||||
channelsList = new ArrayList<>(openChannels);
|
||||
}
|
||||
List<UserConnection> readyServerConnections = null;
|
||||
synchronized(waitingServerConnections) {
|
||||
Iterator<UserConnection> connIterator = waitingServerConnections.iterator();
|
||||
while(connIterator.hasNext()) {
|
||||
UserConnection userCon = connIterator.next();
|
||||
if(userCon.isConnected()) {
|
||||
ServerConnection serverCon = userCon.getServer();
|
||||
if(serverCon != null) {
|
||||
if(readyServerConnections == null) {
|
||||
readyServerConnections = new ArrayList<>(4);
|
||||
}
|
||||
readyServerConnections.add(userCon);
|
||||
connIterator.remove();
|
||||
}
|
||||
}else {
|
||||
connIterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
if(readyServerConnections != null) {
|
||||
for(int i = 0, l = readyServerConnections.size(); i < l; ++i) {
|
||||
handleServerConnectionReady(readyServerConnections.get(i));
|
||||
}
|
||||
}
|
||||
boolean updateLoop = !conf.getUpdateConfig().isBlockAllClientUpdates();
|
||||
final AtomicInteger sizeTracker = updateLoop ? new AtomicInteger(0) : null;
|
||||
final int rateLimitParam = conf.getUpdateConfig().getCertPacketDataRateLimit() / 4;
|
||||
final int serverInfoSendRate = Math.max(conf.getPauseMenuConf().getInfoSendRate(), 1);
|
||||
BungeeScheduler sched = BungeeCord.getInstance().getScheduler();
|
||||
for(Channel c : channelsList) {
|
||||
EaglerConnectionInstance conn = c.attr(EaglerPipeline.CONNECTION_INSTANCE).get();
|
||||
if(conn.userConnection == null) {
|
||||
continue;
|
||||
}
|
||||
final EaglerInitialHandler i = (EaglerInitialHandler)conn.userConnection.getPendingConnection();
|
||||
boolean certToSend = false;
|
||||
if(updateLoop) {
|
||||
synchronized(i.certificatesToSend) {
|
||||
if(!i.certificatesToSend.isEmpty()) {
|
||||
certToSend = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean serverInfoToSend = false;
|
||||
synchronized(i.serverInfoSendBuffer) {
|
||||
if(!i.serverInfoSendBuffer.isEmpty()) {
|
||||
serverInfoToSend = true;
|
||||
}
|
||||
}
|
||||
if(certToSend || serverInfoToSend) {
|
||||
final boolean do_certToSend = certToSend;
|
||||
final boolean do_serverInfoToSend = serverInfoToSend;
|
||||
sched.runAsync(EaglerXBungee.getEagler(), () -> {
|
||||
if(do_certToSend) {
|
||||
ClientCertificateHolder certHolder = null;
|
||||
synchronized(i.certificatesToSend) {
|
||||
if(i.certificatesToSend.size() > 0) {
|
||||
Iterator<ClientCertificateHolder> itr = i.certificatesToSend.iterator();
|
||||
certHolder = itr.next();
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
if(certHolder != null && sizeTracker.getAndAdd(certHolder.data.length) < rateLimitParam) {
|
||||
int identityHash = certHolder.hashCode();
|
||||
boolean bb;
|
||||
synchronized(i.certificatesSent) {
|
||||
bb = i.certificatesSent.add(identityHash);
|
||||
}
|
||||
if(bb) {
|
||||
i.sendEaglerMessage(new SPacketUpdateCertEAG(certHolder.data));
|
||||
}
|
||||
}
|
||||
}
|
||||
if(do_serverInfoToSend) {
|
||||
List<GameMessagePacket> toSend = i.serverInfoSendBuffer;
|
||||
synchronized(toSend) {
|
||||
if(!toSend.isEmpty()) {
|
||||
try {
|
||||
if(serverInfoSendRate == 1) {
|
||||
i.getEaglerMessageController().sendPacketImmediately(toSend.remove(0));
|
||||
}else {
|
||||
for(int j = 0; j < serverInfoSendRate; ++j) {
|
||||
if(!toSend.isEmpty()) {
|
||||
i.getEaglerMessageController().sendPacketImmediately(toSend.remove(0));
|
||||
}else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}catch(Throwable t) {
|
||||
log.log(Level.SEVERE, "Exception in thread \"" + Thread.currentThread().getName() + "\"!", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
for(EaglerListenerConfig lst : conf.getServerListeners()) {
|
||||
HttpWebServer srv = lst.getWebServer();
|
||||
@ -115,46 +239,13 @@ public class EaglerPipeline {
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!conf.getUpdateConfig().isBlockAllClientUpdates()) {
|
||||
int sizeTracker = 0;
|
||||
for(Channel c : channelsList) {
|
||||
EaglerConnectionInstance conn = c.attr(EaglerPipeline.CONNECTION_INSTANCE).get();
|
||||
if(conn.userConnection == null) {
|
||||
continue;
|
||||
}
|
||||
EaglerInitialHandler i = (EaglerInitialHandler)conn.userConnection.getPendingConnection();
|
||||
ClientCertificateHolder certHolder = null;
|
||||
synchronized(i.certificatesToSend) {
|
||||
if(i.certificatesToSend.size() > 0) {
|
||||
Iterator<ClientCertificateHolder> itr = i.certificatesToSend.iterator();
|
||||
certHolder = itr.next();
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
if(certHolder != null) {
|
||||
int identityHash = certHolder.hashCode();
|
||||
boolean bb;
|
||||
synchronized(i.certificatesSent) {
|
||||
bb = i.certificatesSent.add(identityHash);
|
||||
}
|
||||
if(bb) {
|
||||
conn.userConnection.sendData(UPDATE_CERT_CHANNEL, certHolder.data);
|
||||
sizeTracker += certHolder.data.length;
|
||||
if(sizeTracker > (conf.getUpdateConfig().getCertPacketDataRateLimit() / 4)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EaglerUpdateSvc.updateTick();
|
||||
}
|
||||
}catch(Throwable t) {
|
||||
log.severe("Exception in thread \"" + Thread.currentThread().getName() + "\"! " + t.toString());
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public static final ChannelInitializer<Channel> SERVER_CHILD = new ChannelInitializer<Channel>() {
|
||||
|
||||
@Override
|
||||
@ -194,5 +285,34 @@ public class EaglerPipeline {
|
||||
openChannels.remove(channel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void addServerConnectListener(UserConnection player) {
|
||||
synchronized(waitingServerConnections) {
|
||||
waitingServerConnections.add(player);
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleServerConnectionReady(UserConnection userConnection) {
|
||||
try {
|
||||
ServerConnection server = userConnection.getServer();
|
||||
server.sendData(EaglerBackendRPCProtocol.CHANNEL_NAME_READY, ArrayUtils.EMPTY_BYTE_ARRAY);
|
||||
if(userConnection.getPendingConnection() instanceof EaglerInitialHandler) {
|
||||
EaglerInitialHandler handler = (EaglerInitialHandler) userConnection.getPendingConnection();
|
||||
ServerInfo sv = server.getInfo();
|
||||
EaglerXBungee plugin = EaglerXBungee.getEagler();
|
||||
boolean fnawSkins = !plugin.getConfig().getDisableFNAWSkinsEverywhere()
|
||||
&& !plugin.getConfig().getDisableFNAWSkinsOnServersSet().contains(sv.getName());
|
||||
if(fnawSkins != handler.currentFNAWSkinEnableStatus.getAndSet(fnawSkins)) {
|
||||
handler.sendEaglerMessage(new SPacketEnableFNAWSkinsEAG(fnawSkins, false));
|
||||
}
|
||||
if(handler.getEaglerListenerConfig().getEnableVoiceChat()) {
|
||||
plugin.getVoiceService().handleServerConnected(userConnection, sv);
|
||||
}
|
||||
}
|
||||
}catch(Throwable t) {
|
||||
EaglerXBungee.logger().log(Level.SEVERE, "Failed to process server connection ready handler for player \""
|
||||
+ userConnection.getName() + "\"", t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,64 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.Protocol;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class EaglerProtocolAccessProxy {
|
||||
|
||||
private static final Field fieldToClient;
|
||||
private static final Field fieldToServer;
|
||||
private static final Method methodGetId;
|
||||
private static final Method methodCreatePacket;
|
||||
|
||||
static {
|
||||
try {
|
||||
fieldToClient = Protocol.class.getDeclaredField("TO_CLIENT");
|
||||
fieldToClient.setAccessible(true);
|
||||
fieldToServer = Protocol.class.getDeclaredField("TO_SERVER");
|
||||
fieldToServer.setAccessible(true);
|
||||
methodGetId = Protocol.DirectionData.class.getDeclaredMethod("getId", Class.class, int.class);
|
||||
methodGetId.setAccessible(true);
|
||||
methodCreatePacket = Protocol.DirectionData.class.getDeclaredMethod("createPacket", int.class, int.class);
|
||||
methodCreatePacket.setAccessible(true);
|
||||
}catch(Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public static int getPacketId(Protocol protocol, int protocolVersion, DefinedPacket pkt, boolean server) {
|
||||
try {
|
||||
Object prot = server ? fieldToClient.get(protocol) : fieldToServer.get(protocol);
|
||||
return (int)methodGetId.invoke(prot, pkt.getClass(), protocolVersion);
|
||||
}catch(Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public static DefinedPacket createPacket(Protocol protocol, int protocolVersion, int packetId, boolean server) {
|
||||
try {
|
||||
Object prot = server ? fieldToClient.get(protocol) : fieldToServer.get(protocol);
|
||||
return (DefinedPacket) methodCreatePacket.invoke(prot, packetId, protocolVersion);
|
||||
}catch(Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -20,6 +20,7 @@ import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.auth.SHA1Digest;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerUpdateConfig;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler.ClientCertificateHolder;
|
||||
@ -43,9 +44,9 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
*/
|
||||
public class EaglerUpdateSvc {
|
||||
|
||||
private static final List<ClientCertificateHolder> certs = new ArrayList();
|
||||
private static final Map<String,CachedClientCertificate> certsCache = new HashMap();
|
||||
private static final Set<String> deadURLS = new HashSet();
|
||||
private static final List<ClientCertificateHolder> certs = new ArrayList<>();
|
||||
private static final Map<String,CachedClientCertificate> certsCache = new HashMap<>();
|
||||
private static final Set<String> deadURLS = new HashSet<>();
|
||||
|
||||
private static class CachedClientCertificate {
|
||||
private final ClientCertificateHolder cert;
|
||||
@ -61,7 +62,7 @@ public class EaglerUpdateSvc {
|
||||
|
||||
public static void updateTick() {
|
||||
Logger log = EaglerXBungee.logger();
|
||||
long millis = System.currentTimeMillis();
|
||||
long millis = EaglerXBungeeAPIHelper.steadyTimeMillis();
|
||||
EaglerUpdateConfig conf = EaglerXBungee.getEagler().getConfig().getUpdateConfig();
|
||||
if(conf.isDownloadLatestCerts() && millis - lastDownload > (long)conf.getCheckForUpdatesEvery() * 1000l) {
|
||||
lastDownload = millis;
|
||||
@ -72,7 +73,7 @@ public class EaglerUpdateSvc {
|
||||
log.severe("Uncaught exception downloading certificates!");
|
||||
t.printStackTrace();
|
||||
}
|
||||
millis = System.currentTimeMillis();
|
||||
millis = EaglerXBungeeAPIHelper.steadyTimeMillis();
|
||||
}
|
||||
if(conf.isEnableEagcertFolder() && millis - lastEnumerate > 5000l) {
|
||||
lastEnumerate = millis;
|
||||
@ -95,7 +96,7 @@ public class EaglerUpdateSvc {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Set<String> filenames = new HashSet();
|
||||
Set<String> filenames = new HashSet<>();
|
||||
for(String str : conf.getDownloadCertURLs()) {
|
||||
try {
|
||||
URL url = new URL(str);
|
||||
@ -179,7 +180,7 @@ public class EaglerUpdateSvc {
|
||||
}
|
||||
boolean dirty = false;
|
||||
File[] dirList = eagcert.listFiles();
|
||||
Set<String> existingFiles = new HashSet();
|
||||
Set<String> existingFiles = new HashSet<>();
|
||||
for(int i = 0; i < dirList.length; ++i) {
|
||||
File f = dirList[i];
|
||||
String n = f.getName();
|
||||
|
@ -23,11 +23,14 @@ import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftWebSocketOpenEvent;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerRateLimiter;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.RateLimitStatus;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.web.HttpMemoryCache;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.web.HttpWebServer;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.api.event.ClientConnectEvent;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2023 lax1dude. All Rights Reserved.
|
||||
@ -63,12 +66,20 @@ public class HttpHandshakeHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
String rateLimitHost = null;
|
||||
|
||||
SocketAddress addr;
|
||||
if(conf.isForwardIp()) {
|
||||
String str = headers.get(conf.getForwardIpHeader());
|
||||
if(str != null) {
|
||||
rateLimitHost = str.split(",", 2)[0];
|
||||
try {
|
||||
ctx.channel().attr(EaglerPipeline.REAL_ADDRESS).set(InetAddress.getByName(rateLimitHost));
|
||||
InetAddress inetAddr = InetAddress.getByName(rateLimitHost);
|
||||
addr = ctx.channel().remoteAddress();
|
||||
if(addr instanceof InetSocketAddress) {
|
||||
addr = new InetSocketAddress(inetAddr, ((InetSocketAddress)addr).getPort());
|
||||
}else {
|
||||
addr = new InetSocketAddress(inetAddr, 0);
|
||||
}
|
||||
ctx.channel().attr(EaglerPipeline.REAL_ADDRESS).set(inetAddr);
|
||||
}catch(UnknownHostException ex) {
|
||||
EaglerXBungee.logger().warning("[" + ctx.channel().remoteAddress() + "]: Connected with an invalid '" + conf.getForwardIpHeader() + "' header, disconnecting...");
|
||||
ctx.close();
|
||||
@ -80,7 +91,7 @@ public class HttpHandshakeHandler extends ChannelInboundHandlerAdapter {
|
||||
return;
|
||||
}
|
||||
}else {
|
||||
SocketAddress addr = ctx.channel().remoteAddress();
|
||||
addr = ctx.channel().remoteAddress();
|
||||
if(addr instanceof InetSocketAddress) {
|
||||
rateLimitHost = ((InetSocketAddress) addr).getAddress().getHostAddress();
|
||||
}
|
||||
@ -98,17 +109,31 @@ public class HttpHandshakeHandler extends ChannelInboundHandlerAdapter {
|
||||
return;
|
||||
}
|
||||
|
||||
ClientConnectEvent evt = BungeeCord.getInstance().getPluginManager().callEvent(new ClientConnectEvent(addr, conf));
|
||||
if(evt.isCancelled()) {
|
||||
ctx.close();
|
||||
return;
|
||||
}
|
||||
|
||||
if(headers.get(HttpHeaderNames.CONNECTION) != null && headers.get(HttpHeaderNames.CONNECTION).toLowerCase().contains("upgrade") &&
|
||||
"websocket".equalsIgnoreCase(headers.get(HttpHeaderNames.UPGRADE))) {
|
||||
|
||||
|
||||
String origin = headers.get(HttpHeaderNames.ORIGIN);
|
||||
if(origin != null) {
|
||||
ctx.channel().attr(EaglerPipeline.ORIGIN).set(origin);
|
||||
}
|
||||
|
||||
//TODO: origin blacklist
|
||||
String userAgent = headers.get(HttpHeaderNames.USER_AGENT);
|
||||
if(userAgent != null) {
|
||||
ctx.channel().attr(EaglerPipeline.USER_AGENT).set(userAgent);
|
||||
}
|
||||
|
||||
if(ipRateLimit == RateLimitStatus.OK) {
|
||||
EaglercraftWebSocketOpenEvent evt2 = new EaglercraftWebSocketOpenEvent(ctx.channel(), conf, rateLimitHost, origin, userAgent);
|
||||
BungeeCord.getInstance().getPluginManager().callEvent(evt2);
|
||||
if(evt2.isCancelled()) {
|
||||
ctx.close();
|
||||
return;
|
||||
}
|
||||
ctx.channel().attr(EaglerPipeline.HOST).set(headers.get(HttpHeaderNames.HOST));
|
||||
ctx.pipeline().replace(this, "HttpWebSocketHandler", new HttpWebSocketHandler(conf));
|
||||
}
|
||||
|
@ -72,6 +72,7 @@ public abstract class HttpServerQueryHandler extends ChannelInboundHandlerAdapte
|
||||
private boolean acceptBinaryPacket = false;
|
||||
private boolean hasClosed = false;
|
||||
private boolean keepAlive = false;
|
||||
private long maxAge = -1l;
|
||||
|
||||
public void beginHandleQuery(EaglerListenerConfig conf, ChannelHandlerContext context, String accept) {
|
||||
this.conf = conf;
|
||||
@ -188,6 +189,14 @@ public abstract class HttpServerQueryHandler extends ChannelInboundHandlerAdapte
|
||||
return accept;
|
||||
}
|
||||
|
||||
public String getOrigin() {
|
||||
return context.channel().attr(EaglerPipeline.ORIGIN).get();
|
||||
}
|
||||
|
||||
public String getUserAgent() {
|
||||
return context.channel().attr(EaglerPipeline.USER_AGENT).get();
|
||||
}
|
||||
|
||||
public void sendStringResponse(String type, String str) {
|
||||
context.writeAndFlush(new TextWebSocketFrame(QueryManager.createStringResponse(accept, str).toString()));
|
||||
}
|
||||
@ -220,6 +229,14 @@ public abstract class HttpServerQueryHandler extends ChannelInboundHandlerAdapte
|
||||
return keepAlive;
|
||||
}
|
||||
|
||||
public long getMaxAge() {
|
||||
return maxAge;
|
||||
}
|
||||
|
||||
public void setMaxAge(long millis) {
|
||||
this.maxAge = millis;
|
||||
}
|
||||
|
||||
protected abstract void begin(String queryType);
|
||||
|
||||
protected abstract void processString(String str);
|
||||
|
@ -10,12 +10,15 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
@ -39,10 +42,11 @@ import io.netty.util.ReferenceCountUtil;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftClientBrandEvent;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftHandleAuthCookieEvent;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftHandleAuthPasswordEvent;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftIsAuthRequiredEvent;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftIsAuthRequiredEvent.AuthMethod;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftIsAuthRequiredEvent.AuthResponse;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftMOTDEvent;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftRegisterCapeEvent;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftRegisterSkinEvent;
|
||||
@ -55,12 +59,14 @@ import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerRate
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerUpdateConfig;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.RateLimitStatus;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler.ClientCertificateHolder;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.bungeeprotocol.EaglerBungeeProtocol;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.protocol.GameProtocolMessageController;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.query.MOTDQueryHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.query.QueryManager;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.CapePackets;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SkinPackets;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SkinService;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.api.AbstractReconnectHandler;
|
||||
@ -73,9 +79,9 @@ import net.md_5.bungee.api.event.PreLoginEvent;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
import net.md_5.bungee.connection.LoginResult;
|
||||
import net.md_5.bungee.connection.UpstreamBridge;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.netty.HandlerBoss;
|
||||
import net.md_5.bungee.protocol.Property;
|
||||
import net.md_5.bungee.protocol.Protocol;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.event.ServerConnectEvent;
|
||||
@ -103,17 +109,19 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
private int clientProtocolVersion = -1;
|
||||
private boolean isProtocolExchanged = false;
|
||||
private int gameProtocolVersion = -1;
|
||||
private CharSequence clientBrandString;
|
||||
private CharSequence clientVersionString;
|
||||
private CharSequence clientUsername;
|
||||
private String clientBrandString;
|
||||
private String clientVersionString;
|
||||
private String clientUsername;
|
||||
private UUID clientUUID;
|
||||
private UUID offlineUUID;
|
||||
private CharSequence clientRequestedServer;
|
||||
private String clientRequestedServer;
|
||||
private boolean clientAuth;
|
||||
private byte[] clientAuthUsername;
|
||||
private byte[] clientAuthPassword;
|
||||
private boolean clientEnableCookie;
|
||||
private byte[] clientCookieData;
|
||||
private EaglercraftIsAuthRequiredEvent authRequireEvent;
|
||||
private final Map<String, byte[]> profileData = new HashMap();
|
||||
private final Map<String, byte[]> profileData = new HashMap<>();
|
||||
private boolean hasFirstPacket = false;
|
||||
private boolean hasBinaryConnection = false;
|
||||
private boolean connectionClosed = false;
|
||||
@ -122,6 +130,9 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
private Property texturesOverrideProperty;
|
||||
private boolean overrideEaglerToVanillaSkins;
|
||||
|
||||
private static final Set<String> profileDataStandard = Sets.newHashSet(
|
||||
"skin_v1", "skin_v2", "cape_v1", "update_cert_v1", "brand_uuid_v1");
|
||||
|
||||
public HttpWebSocketHandler(EaglerListenerConfig conf) {
|
||||
this.conf = conf;
|
||||
}
|
||||
@ -171,7 +182,7 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
int limit = bungus.config.getPlayerLimit();
|
||||
if (limit > 0 && bungus.getOnlineCount() >= limit) {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE, bungus.getTranslation("proxy_full"))
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -185,7 +196,7 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
if (i >= conf.getMaxPlayer()) {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE, bungus.getTranslation("proxy_full"))
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -260,12 +271,12 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
if(eaglerLegacyProtocolVersion == 1) {
|
||||
if(authConfig.isEnableAuthentication()) {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE, "Please update your client to register on this server!")
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}else if(buffer.readUnsignedByte() != minecraftProtocolVersion) {
|
||||
}else if(buffer.readUnsignedByte() != minecraftProtocolVersion || !conf.isAllowV3()) {
|
||||
clientLoginState = HandshakePacketTypes.STATE_CLIENT_COMPLETE;
|
||||
connectionClosed = true;
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
ByteBuf buf = ctx.alloc().buffer();
|
||||
buf.writeByte(HandshakePacketTypes.PROTOCOL_VERSION_MISMATCH);
|
||||
buf.writeByte(1);
|
||||
buf.writeByte(1);
|
||||
@ -277,32 +288,38 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
return;
|
||||
}
|
||||
}else if(eaglerLegacyProtocolVersion == 2) {
|
||||
int minProtVers = Integer.MAX_VALUE;
|
||||
int maxProtVers = -1;
|
||||
boolean hasV2InList = false;
|
||||
boolean hasV3InList = false;
|
||||
|
||||
int minGameVers = Integer.MAX_VALUE;
|
||||
int maxGameVers = -1;
|
||||
boolean has47InList = false;
|
||||
//make sure to update VersionQueryHandler too
|
||||
int minServerSupported = conf.isAllowV3() ? 2 : 4;
|
||||
int maxServerSupported = conf.isAllowV4() ? 4 : 3;
|
||||
int minAvailableProtVers = Integer.MAX_VALUE;
|
||||
int maxAvailableProtVers = Integer.MIN_VALUE;
|
||||
int minSupportedProtVers = Integer.MAX_VALUE;
|
||||
int maxSupportedProtVers = Integer.MIN_VALUE;
|
||||
|
||||
int cnt = buffer.readUnsignedShort();
|
||||
for(int i = 0; i < cnt; ++i) {
|
||||
int j = buffer.readUnsignedShort();
|
||||
if(j == 2) {
|
||||
hasV2InList = true;
|
||||
if(j > maxAvailableProtVers) {
|
||||
maxAvailableProtVers = j;
|
||||
}
|
||||
if(j == 3) {
|
||||
hasV3InList = true;
|
||||
if(j < minAvailableProtVers) {
|
||||
minAvailableProtVers = j;
|
||||
}
|
||||
if(j > maxProtVers) {
|
||||
maxProtVers = j;
|
||||
}
|
||||
if(j < minProtVers) {
|
||||
minProtVers = j;
|
||||
if(j >= minServerSupported && j <= maxServerSupported) {
|
||||
if(j > maxSupportedProtVers) {
|
||||
maxSupportedProtVers = j;
|
||||
}
|
||||
if(j < minSupportedProtVers) {
|
||||
minSupportedProtVers = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int minGameVers = Integer.MAX_VALUE;
|
||||
int maxGameVers = -1;
|
||||
boolean has47InList = false;
|
||||
|
||||
cnt = buffer.readUnsignedShort();
|
||||
for(int i = 0; i < cnt; ++i) {
|
||||
int j = buffer.readUnsignedShort();
|
||||
@ -317,34 +334,41 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
if(minProtVers == Integer.MAX_VALUE || minGameVers == Integer.MAX_VALUE) {
|
||||
if(maxAvailableProtVers == Integer.MIN_VALUE || maxGameVers == Integer.MIN_VALUE) {
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
boolean versMisMatch = false;
|
||||
boolean isServerProbablyOutdated = false;
|
||||
boolean isClientProbablyOutdated = false;
|
||||
if(!hasV2InList && !hasV3InList) {
|
||||
if(maxSupportedProtVers == Integer.MIN_VALUE) {
|
||||
clientProtocolVersion = maxAvailableProtVers < 3 ? 2 : 3;
|
||||
versMisMatch = true;
|
||||
isServerProbablyOutdated = minProtVers > 3 && maxProtVers > 3; //make sure to update VersionQueryHandler too
|
||||
isClientProbablyOutdated = minProtVers < 2 && maxProtVers < 2;
|
||||
isServerProbablyOutdated = minAvailableProtVers > maxServerSupported && maxAvailableProtVers > maxServerSupported;
|
||||
isClientProbablyOutdated = minAvailableProtVers < minServerSupported && maxAvailableProtVers < minServerSupported;
|
||||
}else if(!has47InList) {
|
||||
clientProtocolVersion = 3;
|
||||
versMisMatch = true;
|
||||
isServerProbablyOutdated = minGameVers > minecraftProtocolVersion && maxGameVers > minecraftProtocolVersion;
|
||||
isClientProbablyOutdated = minGameVers < minecraftProtocolVersion && maxGameVers < minecraftProtocolVersion;
|
||||
}else {
|
||||
clientProtocolVersion = maxSupportedProtVers;
|
||||
}
|
||||
|
||||
clientProtocolVersion = hasV3InList ? 3 : 2;
|
||||
|
||||
if(versMisMatch) {
|
||||
clientLoginState = HandshakePacketTypes.STATE_CLIENT_COMPLETE;
|
||||
connectionClosed = true;
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
ByteBuf buf = ctx.alloc().buffer();
|
||||
buf.writeByte(HandshakePacketTypes.PROTOCOL_VERSION_MISMATCH);
|
||||
|
||||
buf.writeShort(2);
|
||||
buf.writeShort(2); // want v2 or v3
|
||||
buf.writeShort(3);
|
||||
buf.writeShort((conf.isAllowV3() ? 2 : 0) + (conf.isAllowV4() ? 1 : 0));
|
||||
if(conf.isAllowV3()) {
|
||||
buf.writeShort(2);
|
||||
buf.writeShort(3);
|
||||
}
|
||||
if(conf.isAllowV4()) {
|
||||
buf.writeShort(4);
|
||||
}
|
||||
|
||||
buf.writeShort(1);
|
||||
buf.writeShort(minecraftProtocolVersion); // want game version 47
|
||||
@ -357,14 +381,14 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
}
|
||||
}else {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE, "Legacy protocol version should always be '2' on post-snapshot clients")
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
|
||||
int strlen = buffer.readUnsignedByte();
|
||||
CharSequence eaglerBrand = buffer.readCharSequence(strlen, StandardCharsets.US_ASCII);
|
||||
String eaglerBrand = buffer.readCharSequence(strlen, StandardCharsets.US_ASCII).toString();
|
||||
strlen = buffer.readUnsignedByte();
|
||||
CharSequence eaglerVersionString = buffer.readCharSequence(strlen, StandardCharsets.US_ASCII);
|
||||
String eaglerVersionString = buffer.readCharSequence(strlen, StandardCharsets.US_ASCII).toString();
|
||||
|
||||
if(eaglerLegacyProtocolVersion >= 2) {
|
||||
clientAuth = buffer.readBoolean();
|
||||
@ -393,6 +417,19 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
addr = InetAddress.getLoopbackAddress();
|
||||
}
|
||||
}
|
||||
|
||||
EaglercraftClientBrandEvent brandEvent = new EaglercraftClientBrandEvent(eaglerBrand, eaglerVersionString,
|
||||
ctx.channel().attr(EaglerPipeline.ORIGIN).get(), clientProtocolVersion, addr);
|
||||
eaglerXBungee.getProxy().getPluginManager().callEvent(brandEvent);
|
||||
if(brandEvent.isCancelled()) {
|
||||
BaseComponent kickReason = brandEvent.getMessage();
|
||||
if(kickReason == null) {
|
||||
kickReason = new TextComponent("End of stream");
|
||||
}
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE, kickReason)
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean final_useSnapshotFallbackProtocol = useSnapshotFallbackProtocol;
|
||||
Runnable continueThread = () -> {
|
||||
@ -401,7 +438,7 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
clientBrandString = eaglerBrand;
|
||||
clientVersionString = eaglerVersionString;
|
||||
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
ByteBuf buf = ctx.alloc().buffer();
|
||||
buf.writeByte(HandshakePacketTypes.PROTOCOL_SERVER_VERSION);
|
||||
|
||||
if(final_useSnapshotFallbackProtocol) {
|
||||
@ -426,8 +463,9 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
int meth = getAuthMethodId(authRequireEvent.getUseAuthType());
|
||||
|
||||
if(meth == -1) {
|
||||
buf.release();
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE, "Unsupported authentication method resolved")
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
EaglerXBungee.logger().severe("[" + localAddrString + "]: Disconnecting, unsupported AuthMethod: " + authRequireEvent.getUseAuthType());
|
||||
return;
|
||||
}
|
||||
@ -455,28 +493,28 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
clientAuth, clientAuthUsername, (reqAuthEvent) -> {
|
||||
if(authRequireEvent.shouldKickUser()) {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE, authRequireEvent.getKickMessage())
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
|
||||
AuthResponse resp = authRequireEvent.getAuthRequired();
|
||||
EaglercraftIsAuthRequiredEvent.AuthResponse resp = authRequireEvent.getAuthRequired();
|
||||
if(resp == null) {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE, "IsAuthRequiredEvent was not handled")
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
EaglerXBungee.logger().severe("[" + localAddrString + "]: Disconnecting, no installed authentication system handled: " + authRequireEvent.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
if(resp == AuthResponse.DENY) {
|
||||
if(resp == EaglercraftIsAuthRequiredEvent.AuthResponse.DENY) {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE, authRequireEvent.getKickMessage())
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
|
||||
AuthMethod type = authRequireEvent.getUseAuthType();
|
||||
EaglercraftIsAuthRequiredEvent.AuthMethod type = authRequireEvent.getUseAuthType();
|
||||
if(type == null) {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE, "IsAuthRequiredEvent was not fully handled")
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
EaglerXBungee.logger().severe("[" + localAddrString + "]: Disconnecting, no authentication method provided by handler");
|
||||
return;
|
||||
}
|
||||
@ -484,12 +522,12 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
int typeId = getAuthMethodId(type);
|
||||
if(typeId == -1) {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE, "Unsupported authentication method resolved")
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
EaglerXBungee.logger().severe("[" + localAddrString + "]: Disconnecting, unsupported AuthMethod: " + type);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!clientAuth && resp == AuthResponse.REQUIRE) {
|
||||
if(!clientAuth && resp == EaglercraftIsAuthRequiredEvent.AuthResponse.REQUIRE) {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_AUTHENTICATION_REQUIRED,
|
||||
HandshakePacketTypes.AUTHENTICATION_REQUIRED + " [" + typeId + "] " + authRequireEvent.getAuthMessage())
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
@ -498,7 +536,7 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
}else {
|
||||
if(authRequireEvent.getUseAuthType() == null) {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE, "IsAuthRequiredEvent was not fully handled")
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
EaglerXBungee.logger().severe("[" + localAddrString + "]: Disconnecting, no authentication method provided by handler");
|
||||
return;
|
||||
}
|
||||
@ -535,10 +573,9 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
clientLoginState = HandshakePacketTypes.STATE_STALLING;
|
||||
|
||||
int strlen = buffer.readUnsignedByte();
|
||||
clientUsername = buffer.readCharSequence(strlen, StandardCharsets.US_ASCII);
|
||||
clientUsername = buffer.readCharSequence(strlen, StandardCharsets.US_ASCII).toString();
|
||||
|
||||
String usrs = clientUsername.toString();
|
||||
if(!usrs.equals(usrs.replaceAll("[^A-Za-z0-9_]", "_").trim())) {
|
||||
if(!clientUsername.equals(clientUsername.replaceAll("[^A-Za-z0-9_]", "_"))) {
|
||||
sendLoginDenied(ctx, "Invalid characters in username")
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
@ -566,11 +603,28 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
clientUUID = offlineUUID = EaglerInitialHandler.generateOfflineUUID(clientAuthUsername);
|
||||
|
||||
strlen = buffer.readUnsignedByte();
|
||||
clientRequestedServer = buffer.readCharSequence(strlen, StandardCharsets.US_ASCII);
|
||||
clientRequestedServer = buffer.readCharSequence(strlen, StandardCharsets.US_ASCII).toString();
|
||||
strlen = buffer.readUnsignedByte();
|
||||
clientAuthPassword = new byte[strlen];
|
||||
buffer.readBytes(clientAuthPassword);
|
||||
|
||||
if(clientProtocolVersion >= 4) {
|
||||
clientEnableCookie = buffer.readBoolean();
|
||||
strlen = buffer.readUnsignedByte();
|
||||
if(clientEnableCookie && strlen > 0) {
|
||||
clientCookieData = new byte[strlen];
|
||||
buffer.readBytes(clientCookieData);
|
||||
}else {
|
||||
if(strlen > 0) {
|
||||
throw new IllegalArgumentException("Unexpected cookie");
|
||||
}
|
||||
clientCookieData = null;
|
||||
}
|
||||
}else {
|
||||
clientEnableCookie = false;
|
||||
clientCookieData = null;
|
||||
}
|
||||
|
||||
if(buffer.isReadable()) {
|
||||
throw new IllegalArgumentException("Packet too long");
|
||||
}
|
||||
@ -578,16 +632,14 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
Runnable continueThread = () -> {
|
||||
|
||||
final BungeeCord bungee = BungeeCord.getInstance();
|
||||
String usernameStr = clientUsername.toString();
|
||||
final ProxiedPlayer oldName = bungee.getPlayer(usernameStr);
|
||||
final ProxiedPlayer oldName = bungee.getPlayer(clientUsername);
|
||||
if (oldName != null) {
|
||||
sendLoginDenied(ctx, bungee.getTranslation("already_connected_proxy", new Object[0]))
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
sendLoginDenied(ctx, bungee.getTranslation("already_connected_proxy")).addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
|
||||
clientLoginState = HandshakePacketTypes.STATE_CLIENT_LOGIN;
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
ByteBuf buf = ctx.alloc().buffer();
|
||||
buf.writeByte(HandshakePacketTypes.PROTOCOL_SERVER_ALLOW_LOGIN);
|
||||
buf.writeByte(clientUsername.length());
|
||||
buf.writeCharSequence(clientUsername, StandardCharsets.US_ASCII);
|
||||
@ -599,93 +651,172 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
EaglerXBungee eaglerXBungee = EaglerXBungee.getEagler();
|
||||
EaglerAuthConfig authConfig = eaglerXBungee.getConfig().getAuthConfig();
|
||||
|
||||
if(authConfig.isEnableAuthentication() && clientAuth) {
|
||||
if(clientAuthPassword.length == 0) {
|
||||
sendLoginDenied(ctx, "Client provided no authentication code")
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}else {
|
||||
try {
|
||||
EaglercraftHandleAuthPasswordEvent handleEvent = new EaglercraftHandleAuthPasswordEvent(
|
||||
conf, remoteAddress, authRequireEvent.getOriginHeader(), clientAuthUsername,
|
||||
authRequireEvent.getSaltingData(), clientUsername, clientUUID, clientAuthPassword,
|
||||
authRequireEvent.getUseAuthType(), authRequireEvent.getAuthMessage(),
|
||||
(Object) authRequireEvent.getAuthAttachment(), clientRequestedServer.toString(),
|
||||
(handleAuthEvent) -> {
|
||||
|
||||
if(handleAuthEvent.getLoginAllowed() != EaglercraftHandleAuthPasswordEvent.AuthResponse.ALLOW) {
|
||||
sendLoginDenied(ctx, handleAuthEvent.getLoginDeniedMessage()).addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
|
||||
clientUsername = handleAuthEvent.getProfileUsername();
|
||||
clientUUID = handleAuthEvent.getProfileUUID();
|
||||
|
||||
String texPropOverrideValue = handleAuthEvent.getApplyTexturesPropertyValue();
|
||||
if(texPropOverrideValue != null) {
|
||||
String texPropOverrideSig = handleAuthEvent.getApplyTexturesPropertySignature();
|
||||
texturesOverrideProperty = new Property("textures", texPropOverrideValue, texPropOverrideSig);
|
||||
}
|
||||
|
||||
overrideEaglerToVanillaSkins = handleAuthEvent.isOverrideEaglerToVanillaSkins();
|
||||
|
||||
if(authConfig.isEnableAuthentication()) {
|
||||
if(clientAuth && clientAuthPassword.length > 0) {
|
||||
EaglercraftHandleAuthPasswordEvent handleEvent = new EaglercraftHandleAuthPasswordEvent(
|
||||
conf, remoteAddress, authRequireEvent.getOriginHeader(), clientAuthUsername,
|
||||
authRequireEvent.getSaltingData(), clientUsername, clientUUID,
|
||||
clientAuthPassword, clientEnableCookie, clientCookieData,
|
||||
authRequireEvent.getUseAuthType(), authRequireEvent.getAuthMessage(),
|
||||
(Object) authRequireEvent.getAuthAttachment(), clientRequestedServer,
|
||||
(handleAuthEvent) -> {
|
||||
|
||||
if(handleAuthEvent.getLoginAllowed() != EaglercraftHandleAuthPasswordEvent.AuthResponse.ALLOW) {
|
||||
sendLoginDenied(ctx, handleAuthEvent.getLoginDeniedMessage()).addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
|
||||
clientUsername = handleAuthEvent.getProfileUsername();
|
||||
clientUUID = handleAuthEvent.getProfileUUID();
|
||||
|
||||
String texPropOverrideValue = handleAuthEvent.getApplyTexturesPropertyValue();
|
||||
if(texPropOverrideValue != null) {
|
||||
String texPropOverrideSig = handleAuthEvent.getApplyTexturesPropertySignature();
|
||||
texturesOverrideProperty = new Property("textures", texPropOverrideValue, texPropOverrideSig);
|
||||
}
|
||||
|
||||
overrideEaglerToVanillaSkins = handleAuthEvent.isOverrideEaglerToVanillaSkins();
|
||||
|
||||
continueThread.run();
|
||||
});
|
||||
|
||||
if(authConfig.isUseBuiltInAuthentication()) {
|
||||
DefaultAuthSystem authSystem = eaglerXBungee.getAuthService();
|
||||
if(authSystem != null) {
|
||||
authSystem.handleAuthPasswordEvent(handleEvent);
|
||||
}
|
||||
}else {
|
||||
eaglerXBungee.getProxy().getPluginManager().callEvent(handleEvent);
|
||||
}
|
||||
|
||||
if(!handleEvent.isAsyncContinue()) {
|
||||
handleEvent.doDirectContinue();
|
||||
}
|
||||
}else if(authRequireEvent.getEnableCookieAuth()) {
|
||||
EaglercraftHandleAuthCookieEvent handleEvent = new EaglercraftHandleAuthCookieEvent(
|
||||
conf, remoteAddress, authRequireEvent.getOriginHeader(), clientAuthUsername,
|
||||
clientUsername, clientUUID, clientEnableCookie, clientCookieData,
|
||||
authRequireEvent.getUseAuthType(), authRequireEvent.getAuthMessage(),
|
||||
(Object) authRequireEvent.getAuthAttachment(),
|
||||
clientRequestedServer, (handleAuthEvent) -> {
|
||||
|
||||
EaglercraftHandleAuthCookieEvent.AuthResponse resp = handleAuthEvent.getLoginAllowed();
|
||||
|
||||
if(resp == null) {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE, "EaglercraftHandleAuthCookieEvent was not handled")
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
EaglerXBungee.logger().severe("[" + localAddrString + "]: Disconnecting, no installed authentication system handled: " + handleAuthEvent.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
if(resp == EaglercraftHandleAuthCookieEvent.AuthResponse.DENY) {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE, handleAuthEvent.getLoginDeniedMessage())
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
|
||||
clientUsername = handleAuthEvent.getProfileUsername();
|
||||
clientUUID = handleAuthEvent.getProfileUUID();
|
||||
|
||||
String texPropOverrideValue = handleAuthEvent.getApplyTexturesPropertyValue();
|
||||
if(texPropOverrideValue != null) {
|
||||
String texPropOverrideSig = handleAuthEvent.getApplyTexturesPropertySignature();
|
||||
texturesOverrideProperty = new Property("textures", texPropOverrideValue, texPropOverrideSig);
|
||||
}
|
||||
|
||||
overrideEaglerToVanillaSkins = handleAuthEvent.isOverrideEaglerToVanillaSkins();
|
||||
|
||||
if(resp == EaglercraftHandleAuthCookieEvent.AuthResponse.ALLOW) {
|
||||
continueThread.run();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if(authConfig.isUseBuiltInAuthentication()) {
|
||||
DefaultAuthSystem authSystem = eaglerXBungee.getAuthService();
|
||||
if(authSystem != null) {
|
||||
authSystem.handleAuthPasswordEvent(handleEvent);
|
||||
}
|
||||
if(!clientAuth && resp == EaglercraftHandleAuthCookieEvent.AuthResponse.REQUIRE_AUTH) {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_AUTHENTICATION_REQUIRED, HandshakePacketTypes.AUTHENTICATION_REQUIRED
|
||||
+ " [" + getAuthMethodId(authRequireEvent.getUseAuthType()) + "] " + authRequireEvent.getAuthMessage())
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
EaglerXBungee.logger().info("[" + localAddrString + "]: Displaying authentication screen");
|
||||
return;
|
||||
}else {
|
||||
eaglerXBungee.getProxy().getPluginManager().callEvent(handleEvent);
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE, "Failed to handle authentication!")
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!handleEvent.isAsyncContinue()) {
|
||||
handleEvent.doDirectContinue();
|
||||
}
|
||||
}catch(Throwable t) {
|
||||
throw new EventException(t);
|
||||
});
|
||||
|
||||
eaglerXBungee.getProxy().getPluginManager().callEvent(handleEvent);
|
||||
|
||||
if(!handleEvent.isAsyncContinue()) {
|
||||
handleEvent.doDirectContinue();
|
||||
}
|
||||
}else {
|
||||
if(authRequireEvent.getAuthRequired() != EaglercraftIsAuthRequiredEvent.AuthResponse.SKIP) {
|
||||
sendLoginDenied(ctx, "Client provided no authentication code").addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}else {
|
||||
continueThread.run();
|
||||
}
|
||||
}
|
||||
}else {
|
||||
continueThread.run();
|
||||
}
|
||||
|
||||
}else {
|
||||
clientLoginState = HandshakePacketTypes.STATE_CLIENT_COMPLETE;
|
||||
sendErrorWrong(ctx, op, "STATE_CLIENT_VERSION")
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
sendErrorWrong(ctx, op, "STATE_CLIENT_VERSION").addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HandshakePacketTypes.PROTOCOL_CLIENT_PROFILE_DATA: {
|
||||
if(clientLoginState == HandshakePacketTypes.STATE_CLIENT_LOGIN) {
|
||||
|
||||
if(profileData.size() > 12) {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_EXCESSIVE_PROFILE_DATA, "Too many profile data packets recieved")
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
|
||||
int strlen = buffer.readUnsignedByte();
|
||||
String dataType = buffer.readCharSequence(strlen, StandardCharsets.US_ASCII).toString();
|
||||
strlen = buffer.readUnsignedShort();
|
||||
byte[] readData = new byte[strlen];
|
||||
buffer.readBytes(readData);
|
||||
|
||||
if(buffer.isReadable()) {
|
||||
throw new IllegalArgumentException("Packet too long");
|
||||
}
|
||||
|
||||
if(!profileData.containsKey(dataType)) {
|
||||
profileData.put(dataType, readData);
|
||||
if(clientProtocolVersion <= 3) {
|
||||
if(profileData.size() >= 12) {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_EXCESSIVE_PROFILE_DATA, "Too many profile data packets recieved")
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
int strlen = buffer.readUnsignedByte();
|
||||
String dataType = buffer.readCharSequence(strlen, StandardCharsets.US_ASCII).toString();
|
||||
strlen = buffer.readUnsignedShort();
|
||||
byte[] readData = new byte[strlen];
|
||||
buffer.readBytes(readData);
|
||||
|
||||
if(buffer.isReadable()) {
|
||||
throw new IllegalArgumentException("Packet too long");
|
||||
}
|
||||
|
||||
if(!profileData.containsKey(dataType)) {
|
||||
profileData.put(dataType, readData);
|
||||
}else {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_DUPLICATE_PROFILE_DATA, "Multiple profile data packets of the same type recieved")
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
}else {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_DUPLICATE_PROFILE_DATA, "Multiple profile data packets of the same type recieved")
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
int count = buffer.readUnsignedByte();
|
||||
if(profileData.size() + count > 12) {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_EXCESSIVE_PROFILE_DATA, "Too many profile data packets recieved")
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
for(int i = 0; i < count; ++i) {
|
||||
int strlen = buffer.readUnsignedByte();
|
||||
String dataType = buffer.readCharSequence(strlen, StandardCharsets.US_ASCII).toString();
|
||||
strlen = buffer.readUnsignedShort();
|
||||
byte[] readData = new byte[strlen];
|
||||
buffer.readBytes(readData);
|
||||
if(!profileData.containsKey(dataType)) {
|
||||
profileData.put(dataType, readData);
|
||||
}else {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_DUPLICATE_PROFILE_DATA, "Multiple profile data packets of the same type recieved")
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(buffer.isReadable()) {
|
||||
throw new IllegalArgumentException("Packet too long");
|
||||
}
|
||||
}
|
||||
|
||||
}else {
|
||||
clientLoginState = HandshakePacketTypes.STATE_CLIENT_COMPLETE;
|
||||
sendErrorWrong(ctx, op, "STATE_CLIENT_LOGIN").addListener(ChannelFutureListener.CLOSE);
|
||||
@ -710,7 +841,7 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
default:
|
||||
clientLoginState = HandshakePacketTypes.STATE_CLIENT_COMPLETE;
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_UNKNOWN_PACKET, "Unknown Packet #" + op)
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
break;
|
||||
}
|
||||
}catch(Throwable ex) {
|
||||
@ -731,7 +862,7 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
int limit = bungee.config.getPlayerLimit();
|
||||
if (limit > 0 && bungee.getOnlineCount() >= limit) {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE, bungee.getTranslation("proxy_full"))
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -745,7 +876,7 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
if (i >= conf.getMaxPlayer()) {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE, bungee.getTranslation("proxy_full"))
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -754,17 +885,17 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
final ProxiedPlayer oldName = bungee.getPlayer(usernameStr);
|
||||
if (oldName != null) {
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE,
|
||||
bungee.getTranslation("already_connected_proxy", new Object[0]))
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
bungee.getTranslation("already_connected_proxy")).addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
final ChannelWrapper ch = new EaglerChannelWrapper(ctx);
|
||||
final EaglerChannelWrapper ch = new EaglerChannelWrapper(ctx);
|
||||
InetSocketAddress baseAddress = (InetSocketAddress)ctx.channel().remoteAddress();
|
||||
InetAddress addr = ctx.channel().attr(EaglerPipeline.REAL_ADDRESS).get();
|
||||
if(addr != null) {
|
||||
baseAddress = new InetSocketAddress(addr, baseAddress.getPort());
|
||||
ch.setRemoteAddress(baseAddress);
|
||||
}
|
||||
|
||||
EaglerUpdateConfig updateconf = EaglerXBungee.getEagler().getConfig().getUpdateConfig();
|
||||
boolean blockUpdate = updateconf.isBlockAllClientUpdates();
|
||||
ClientCertificateHolder cert = null;
|
||||
@ -774,9 +905,34 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
EaglerUpdateSvc.sendCertificateToPlayers(cert = EaglerUpdateSvc.tryMakeHolder(b));
|
||||
}
|
||||
}
|
||||
final EaglerInitialHandler initialHandler = new EaglerInitialHandler(bungee, conf, ch, gameProtocolVersion,
|
||||
UUID clientBrandUUID = null;
|
||||
String clientBrandAsString = clientBrandString.toString();
|
||||
byte[] brandUUIDBytes = profileData.get("brand_uuid_v1");
|
||||
if(brandUUIDBytes != null) {
|
||||
if(brandUUIDBytes.length == 16) {
|
||||
ByteBuf buf = Unpooled.wrappedBuffer(brandUUIDBytes);
|
||||
clientBrandUUID = new UUID(buf.readLong(), buf.readLong());
|
||||
if (clientBrandUUID.equals(EaglerXBungeeAPIHelper.BRAND_NULL_UUID)
|
||||
|| clientBrandUUID.equals(EaglerXBungeeAPIHelper.BRAND_PENDING_UUID)
|
||||
|| clientBrandUUID.equals(EaglerXBungeeAPIHelper.BRAND_VANILLA_UUID)) {
|
||||
clientBrandUUID = null;
|
||||
}
|
||||
}
|
||||
}else {
|
||||
clientBrandUUID = EaglerXBungeeAPIHelper.makeClientBrandUUIDLegacy(clientBrandAsString);
|
||||
}
|
||||
Map<String,byte[]> otherProfileData = new HashMap<>();
|
||||
for(Entry<String,byte[]> etr2 : profileData.entrySet()) {
|
||||
String str = etr2.getKey();
|
||||
if(!profileDataStandard.contains(str)) {
|
||||
otherProfileData.put(str, etr2.getValue());
|
||||
}
|
||||
}
|
||||
final EaglerInitialHandler initialHandler = new EaglerInitialHandler(bungee, conf, ch, clientProtocolVersion,
|
||||
gameProtocolVersion, clientBrandAsString, clientVersionString.toString(), clientBrandUUID,
|
||||
usernameStr, clientUUID, offlineUUID, baseAddress, ctx.channel().attr(EaglerPipeline.HOST).get(),
|
||||
ctx.channel().attr(EaglerPipeline.ORIGIN).get(), cert);
|
||||
ctx.channel().attr(EaglerPipeline.ORIGIN).get(), ctx.channel().attr(EaglerPipeline.USER_AGENT).get(),
|
||||
cert, clientEnableCookie, clientCookieData, otherProfileData);
|
||||
if(!blockUpdate) {
|
||||
List<ClientCertificateHolder> set = EaglerUpdateSvc.getCertList();
|
||||
synchronized(set) {
|
||||
@ -794,10 +950,9 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
final Callback<LoginEvent> complete = (Callback<LoginEvent>) new Callback<LoginEvent>() {
|
||||
public void done(final LoginEvent result, final Throwable error) {
|
||||
if (result.isCancelled()) {
|
||||
final BaseComponent[] reason = result.getCancelReasonComponents();
|
||||
final BaseComponent reason = result.getReason();
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE,
|
||||
ComponentSerializer.toString(reason != null ? reason
|
||||
: TextComponent.fromLegacyText(bungee.getTranslation("kick_message", new Object[0]))))
|
||||
reason != null ? reason : TextComponent.fromLegacy(bungee.getTranslation("kick_message")))
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
}
|
||||
@ -805,7 +960,7 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
return;
|
||||
}
|
||||
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
ByteBuf buf = ctx.alloc().buffer();
|
||||
buf.writeByte(HandshakePacketTypes.PROTOCOL_SERVER_FINISH_LOGIN);
|
||||
ctx.writeAndFlush(new BinaryWebSocketFrame(buf)).addListener(new GenericFutureListener<Future<Void>>() {
|
||||
|
||||
@ -817,6 +972,10 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
final UserConnection userCon = new UserConnection(bungee, ch, usernameStr, initialHandler);
|
||||
userCon.setCompressionThreshold(-1);
|
||||
initialHandler.messageProtocolController = new GameProtocolMessageController(userCon,
|
||||
GamePluginMessageProtocol.getByVersion(clientProtocolVersion),
|
||||
GameProtocolMessageController.createServerHandler(clientProtocolVersion, userCon,
|
||||
EaglerXBungee.getEagler()), conf.getDefragSendDelay());
|
||||
try {
|
||||
if (!userCon.init()) {
|
||||
userCon.disconnect(bungee.getTranslation("already_connected_proxy"));
|
||||
@ -845,19 +1004,18 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
pp.addBefore("HandlerBoss", "ReadTimeoutHandler", new ReadTimeoutHandler((BungeeCord.getInstance()).config.getTimeout(), TimeUnit.MILLISECONDS));
|
||||
|
||||
pp.addBefore("HandlerBoss", "EaglerMinecraftDecoder", new EaglerMinecraftDecoder(
|
||||
EaglerBungeeProtocol.GAME, false, gameProtocolVersion));
|
||||
pp.addBefore("HandlerBoss", "EaglerMinecraftDecoder", new EaglerMinecraftDecoder(Protocol.GAME, false, gameProtocolVersion));
|
||||
|
||||
pp.addBefore("HandlerBoss", "EaglerMinecraftByteBufEncoder", new EaglerMinecraftByteBufEncoder());
|
||||
|
||||
pp.addBefore("HandlerBoss", "EaglerMinecraftWrappedEncoder", new EaglerMinecraftWrappedEncoder());
|
||||
|
||||
pp.addBefore("HandlerBoss", "EaglerMinecraftEncoder", new EaglerMinecraftEncoder(
|
||||
EaglerBungeeProtocol.GAME, true, gameProtocolVersion));
|
||||
pp.addBefore("HandlerBoss", "EaglerMinecraftEncoder", new EaglerMinecraftEncoder(Protocol.GAME, true, gameProtocolVersion));
|
||||
|
||||
boolean doRegisterSkins = true;
|
||||
boolean doForceSkins = false;
|
||||
|
||||
EaglercraftRegisterSkinEvent registerSkinEvent = new EaglercraftRegisterSkinEvent(usernameStr, clientUUID);
|
||||
EaglercraftRegisterSkinEvent registerSkinEvent = new EaglercraftRegisterSkinEvent(usernameStr, clientUUID, authRequireEvent != null ? authRequireEvent.getAuthAttachment() : null);
|
||||
|
||||
bungee.getPluginManager().callEvent(registerSkinEvent);
|
||||
|
||||
@ -866,20 +1024,20 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
if(prop != null) {
|
||||
texturesOverrideProperty = prop;
|
||||
overrideEaglerToVanillaSkins = true;
|
||||
if(clientProtocolVersion >= 4 && (EaglerXBungee.getEagler().getSkinService() instanceof SkinService)) {
|
||||
doForceSkins = true;
|
||||
}
|
||||
}else {
|
||||
if(useExistingProp) {
|
||||
overrideEaglerToVanillaSkins = true;
|
||||
}else {
|
||||
byte[] custom = registerSkinEvent.getForceSetUseCustomPacket();
|
||||
if(custom != null) {
|
||||
profileData.remove("skin_v2");
|
||||
profileData.put("skin_v1", custom);
|
||||
overrideEaglerToVanillaSkins = false;
|
||||
}else {
|
||||
String customUrl = registerSkinEvent.getForceSetUseURL();
|
||||
if(customUrl != null) {
|
||||
EaglerXBungee.getEagler().getSkinService().registerTextureToPlayerAssociation(customUrl, initialHandler.getUniqueId());
|
||||
doRegisterSkins = false;
|
||||
overrideEaglerToVanillaSkins = false;
|
||||
if(clientProtocolVersion >= 4) {
|
||||
doForceSkins = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -887,14 +1045,13 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
EaglerBungeeConfig eaglerConf = EaglerXBungee.getEagler().getConfig();
|
||||
|
||||
|
||||
if(texturesOverrideProperty != null) {
|
||||
LoginResult oldProfile = initialHandler.getLoginProfile();
|
||||
if(oldProfile == null) {
|
||||
oldProfile = new LoginResult(initialHandler.getUniqueId().toString(), initialHandler.getName(), null);
|
||||
initialHandler.setLoginProfile(oldProfile);
|
||||
}
|
||||
oldProfile.setProperties(new Property[] { texturesOverrideProperty, EaglerBungeeConfig.isEaglerProperty });
|
||||
oldProfile.setProperties(eaglerConf.getEnableIsEaglerPlayerProperty() ? new Property[] { texturesOverrideProperty, EaglerBungeeConfig.isEaglerProperty } : new Property[] { texturesOverrideProperty });
|
||||
}else {
|
||||
if(!useExistingProp) {
|
||||
String vanillaSkin = eaglerConf.getEaglerPlayersVanillaSkin();
|
||||
@ -928,6 +1085,9 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
}
|
||||
}
|
||||
doRegisterSkins = false;
|
||||
if(clientProtocolVersion >= 4) {
|
||||
doForceSkins = true;
|
||||
}
|
||||
}catch(Throwable t) {
|
||||
}
|
||||
break;
|
||||
@ -938,10 +1098,18 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
}
|
||||
|
||||
if(doRegisterSkins) {
|
||||
if(profileData.containsKey("skin_v1")) {
|
||||
if(clientProtocolVersion >= 4 && profileData.containsKey("skin_v2")) {
|
||||
try {
|
||||
SkinPackets.registerEaglerPlayer(clientUUID, profileData.get("skin_v2"),
|
||||
EaglerXBungee.getEagler().getSkinService(), 4);
|
||||
} catch (Throwable ex) {
|
||||
SkinPackets.registerEaglerPlayerFallback(clientUUID, EaglerXBungee.getEagler().getSkinService());
|
||||
EaglerXBungee.logger().info("[" + ctx.channel().remoteAddress() + "]: Invalid skin packet: " + ex.toString());
|
||||
}
|
||||
}else if(profileData.containsKey("skin_v1")) {
|
||||
try {
|
||||
SkinPackets.registerEaglerPlayer(clientUUID, profileData.get("skin_v1"),
|
||||
EaglerXBungee.getEagler().getSkinService());
|
||||
EaglerXBungee.getEagler().getSkinService(), 3);
|
||||
} catch (Throwable ex) {
|
||||
SkinPackets.registerEaglerPlayerFallback(clientUUID, EaglerXBungee.getEagler().getSkinService());
|
||||
EaglerXBungee.logger().info("[" + ctx.channel().remoteAddress() + "]: Invalid skin packet: " + ex.toString());
|
||||
@ -950,8 +1118,11 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
SkinPackets.registerEaglerPlayerFallback(clientUUID, EaglerXBungee.getEagler().getSkinService());
|
||||
}
|
||||
}
|
||||
if(doForceSkins) {
|
||||
EaglerXBungee.getEagler().getSkinService().processForceSkin(clientUUID, initialHandler);
|
||||
}
|
||||
|
||||
EaglercraftRegisterCapeEvent registerCapeEvent = new EaglercraftRegisterCapeEvent(usernameStr, clientUUID);
|
||||
EaglercraftRegisterCapeEvent registerCapeEvent = new EaglercraftRegisterCapeEvent(usernameStr, clientUUID, authRequireEvent != null ? authRequireEvent.getAuthAttachment() : null);
|
||||
|
||||
bungee.getPluginManager().callEvent(registerCapeEvent);
|
||||
|
||||
@ -971,11 +1142,21 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
}else {
|
||||
CapePackets.registerEaglerPlayerFallback(clientUUID, EaglerXBungee.getEagler().getCapeService());
|
||||
}
|
||||
if(forceCape != null && clientProtocolVersion >= 4) {
|
||||
EaglerXBungee.getEagler().getCapeService().processForceCape(clientUUID, initialHandler);
|
||||
}
|
||||
|
||||
if(conf.getEnableVoiceChat()) {
|
||||
EaglerXBungee.getEagler().getVoiceService().handlePlayerLoggedIn(userCon);
|
||||
}
|
||||
|
||||
if(clientProtocolVersion >= 4) {
|
||||
GameMessagePacket pauseMenuPkt = EaglerXBungee.getEagler().getConfig().getPauseMenuConf().getPacket();
|
||||
if(pauseMenuPkt != null) {
|
||||
initialHandler.sendEaglerMessage(pauseMenuPkt);
|
||||
}
|
||||
}
|
||||
|
||||
ServerInfo server;
|
||||
if (bungee.getReconnectHandler() != null) {
|
||||
server = bungee.getReconnectHandler().getServer((ProxiedPlayer) userCon);
|
||||
@ -1015,10 +1196,9 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
final Callback<PreLoginEvent> completePre = new Callback<PreLoginEvent>() {
|
||||
public void done(PreLoginEvent var1, Throwable var2) {
|
||||
if (var1.isCancelled()) {
|
||||
final BaseComponent[] reason = var1.getCancelReasonComponents();
|
||||
final BaseComponent reason = var1.getReason();
|
||||
sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE,
|
||||
ComponentSerializer.toString(reason != null ? reason
|
||||
: TextComponent.fromLegacyText(bungee.getTranslation("kick_message", new Object[0]))))
|
||||
reason != null ? reason : TextComponent.fromLegacy(bungee.getTranslation("kick_message")))
|
||||
.addListener(ChannelFutureListener.CLOSE);
|
||||
}else {
|
||||
bungee.getPluginManager().callEvent(new LoginEvent(initialHandler, complete));
|
||||
@ -1109,6 +1289,8 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
if(!handler.isClosed() && !handler.shouldKeepAlive()) {
|
||||
connectionClosed = true;
|
||||
handler.close();
|
||||
}else {
|
||||
ctx.channel().attr(EaglerPipeline.CONNECTION_INSTANCE).get().queryHandler = handler;
|
||||
}
|
||||
}else {
|
||||
connectionClosed = true;
|
||||
@ -1121,7 +1303,7 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
private int getAuthMethodId(AuthMethod meth) {
|
||||
private int getAuthMethodId(EaglercraftIsAuthRequiredEvent.AuthMethod meth) {
|
||||
switch(meth) {
|
||||
case PLAINTEXT:
|
||||
return 255; // plaintext authentication
|
||||
@ -1136,13 +1318,13 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private ChannelFuture sendLoginDenied(ChannelHandlerContext ctx, String reason) {
|
||||
if((!isProtocolExchanged || clientProtocolVersion == 2) && reason.length() > 255) {
|
||||
reason = reason.substring(0, 256);
|
||||
reason = reason.substring(0, 255);
|
||||
}else if(reason.length() > 65535) {
|
||||
reason = reason.substring(0, 65536);
|
||||
reason = reason.substring(0, 65535);
|
||||
}
|
||||
clientLoginState = HandshakePacketTypes.STATE_CLIENT_COMPLETE;
|
||||
connectionClosed = true;
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
ByteBuf buf = ctx.alloc().buffer();
|
||||
buf.writeByte(HandshakePacketTypes.PROTOCOL_SERVER_DENY_LOGIN);
|
||||
byte[] msg = reason.getBytes(StandardCharsets.UTF_8);
|
||||
if(!isProtocolExchanged || clientProtocolVersion == 2) {
|
||||
@ -1157,16 +1339,24 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||
private ChannelFuture sendErrorWrong(ChannelHandlerContext ctx, int op, String state) {
|
||||
return sendErrorCode(ctx, HandshakePacketTypes.SERVER_ERROR_WRONG_PACKET, "Wrong Packet #" + op + " in state '" + state + "'");
|
||||
}
|
||||
|
||||
|
||||
private ChannelFuture sendErrorCode(ChannelHandlerContext ctx, int code, BaseComponent comp) {
|
||||
if((!isProtocolExchanged || clientProtocolVersion == 2)) {
|
||||
return sendErrorCode(ctx, code, ComponentSerializer.toString(comp));
|
||||
}else {
|
||||
return sendErrorCode(ctx, code, comp.toLegacyText());
|
||||
}
|
||||
}
|
||||
|
||||
private ChannelFuture sendErrorCode(ChannelHandlerContext ctx, int code, String str) {
|
||||
if((!isProtocolExchanged || clientProtocolVersion == 2) && str.length() > 255) {
|
||||
str = str.substring(0, 256);
|
||||
str = str.substring(0, 255);
|
||||
}else if(str.length() > 65535) {
|
||||
str = str.substring(0, 65536);
|
||||
str = str.substring(0, 65535);
|
||||
}
|
||||
clientLoginState = HandshakePacketTypes.STATE_CLIENT_COMPLETE;
|
||||
connectionClosed = true;
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
ByteBuf buf = ctx.alloc().buffer();
|
||||
buf.writeByte(HandshakePacketTypes.PROTOCOL_SERVER_ERROR);
|
||||
buf.writeByte(code);
|
||||
byte[] msg = str.getBytes(StandardCharsets.UTF_8);
|
||||
|
@ -0,0 +1,295 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.backend_rpc_protocol;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.EaglerBackendRPCProtocol;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.EaglerBackendRPCHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.EaglerBackendRPCPacket;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.WrongRPCPacketException;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.client.CPacketRPCEnabled;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.client.CPacketRPCSubscribeEvents;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.server.SPacketRPCEnabledFailure;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.server.SPacketRPCEnabledSuccess;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.server.SPacketRPCEventToggledVoice;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.server.SPacketRPCEventWebViewOpenClose;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EnumVoiceState;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.voice.VoiceService;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.ReusableByteArrayInputStream;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.ReusableByteArrayOutputStream;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.Server;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class BackendRPCSessionHandler {
|
||||
|
||||
public static BackendRPCSessionHandler createForPlayer(EaglerInitialHandler eaglerHandler) {
|
||||
return new BackendRPCSessionHandler(eaglerHandler);
|
||||
}
|
||||
|
||||
protected final EaglerInitialHandler eaglerHandler;
|
||||
private Server currentServer = null;
|
||||
private EaglerBackendRPCProtocol currentProtocol = null;
|
||||
private EaglerBackendRPCHandler currentHandler = null;
|
||||
private int subscribedEvents = 0;
|
||||
private final AtomicInteger currentVoiceState = new AtomicInteger(SPacketRPCEventToggledVoice.VOICE_STATE_SERVER_DISABLE);
|
||||
private final ReentrantLock inputStreamLock = new ReentrantLock();
|
||||
private final ReentrantLock outputStreamLock = new ReentrantLock();
|
||||
private final ReusableByteArrayInputStream reusableInputStream = new ReusableByteArrayInputStream();
|
||||
private final ReusableByteArrayOutputStream reusableOutputStream = new ReusableByteArrayOutputStream();
|
||||
private final DataInputStream dataInputStream = new DataInputStream(reusableInputStream);
|
||||
private final DataOutputStream dataOutputStream = new DataOutputStream(reusableOutputStream);
|
||||
|
||||
private BackendRPCSessionHandler(EaglerInitialHandler eaglerHandler) {
|
||||
this.eaglerHandler = eaglerHandler;
|
||||
}
|
||||
|
||||
public void handleRPCPacket(Server server, byte[] data) {
|
||||
synchronized(this) {
|
||||
if(currentServer != null) {
|
||||
if(currentServer != server) {
|
||||
return;
|
||||
}
|
||||
}else {
|
||||
handleCreateContext(server, data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
EaglerBackendRPCPacket packet;
|
||||
try {
|
||||
packet = decodeRPCPacket(currentProtocol, data);
|
||||
} catch (IOException e) {
|
||||
EaglerXBungee.logger().log(Level.SEVERE, "[" + eaglerHandler.getSocketAddress()
|
||||
+ "]: Recieved invalid backend RPC protocol packet for user \"" + eaglerHandler.getName() + "\"", e);
|
||||
return;
|
||||
}
|
||||
packet.handlePacket(currentHandler);
|
||||
}
|
||||
|
||||
protected EaglerBackendRPCPacket decodeRPCPacket(EaglerBackendRPCProtocol protocol, byte[] data) throws IOException {
|
||||
EaglerBackendRPCPacket ret;
|
||||
if(inputStreamLock.tryLock()) {
|
||||
try {
|
||||
reusableInputStream.feedBuffer(data);
|
||||
ret = protocol.readPacket(dataInputStream, EaglerBackendRPCProtocol.CLIENT_TO_SERVER);
|
||||
}finally {
|
||||
inputStreamLock.unlock();
|
||||
}
|
||||
}else {
|
||||
ReusableByteArrayInputStream bai = new ReusableByteArrayInputStream();
|
||||
bai.feedBuffer(data);
|
||||
ret = protocol.readPacket(new DataInputStream(bai), EaglerBackendRPCProtocol.CLIENT_TO_SERVER);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void sendRPCPacket(EaglerBackendRPCPacket packet) {
|
||||
if(currentServer != null) {
|
||||
sendRPCPacket(currentProtocol, currentServer, packet);
|
||||
}else {
|
||||
EaglerXBungee.logger()
|
||||
.warning("[" + eaglerHandler.getSocketAddress()
|
||||
+ "]: Failed to write backend RPC protocol version for user \"" + eaglerHandler.getName()
|
||||
+ "\", the RPC connection is not initialized!");
|
||||
}
|
||||
}
|
||||
|
||||
protected void sendRPCPacket(EaglerBackendRPCProtocol protocol, Server server, EaglerBackendRPCPacket packet) {
|
||||
byte[] ret;
|
||||
int len = packet.length() + 1;
|
||||
if(outputStreamLock.tryLock()) {
|
||||
try {
|
||||
reusableOutputStream.feedBuffer(new byte[len > 0 ? len : 64]);
|
||||
try {
|
||||
protocol.writePacket(dataOutputStream, EaglerBackendRPCProtocol.SERVER_TO_CLIENT, packet);
|
||||
}catch(IOException ex) {
|
||||
throw new IllegalStateException("Failed to serialize packet: " + packet.getClass().getSimpleName(), ex);
|
||||
}
|
||||
ret = reusableOutputStream.returnBuffer();
|
||||
}finally {
|
||||
outputStreamLock.unlock();
|
||||
}
|
||||
}else {
|
||||
ReusableByteArrayOutputStream bao = new ReusableByteArrayOutputStream();
|
||||
bao.feedBuffer(new byte[len > 0 ? len : 64]);
|
||||
try {
|
||||
protocol.writePacket(new DataOutputStream(bao), EaglerBackendRPCProtocol.SERVER_TO_CLIENT, packet);
|
||||
}catch(IOException ex) {
|
||||
throw new IllegalStateException("Failed to serialize packet: " + packet.getClass().getSimpleName(), ex);
|
||||
}
|
||||
ret = bao.returnBuffer();
|
||||
}
|
||||
if(len > 0 && len != ret.length) {
|
||||
EaglerXBungee.logger()
|
||||
.warning("[" + eaglerHandler.getSocketAddress() + "]: Backend RPC packet type "
|
||||
+ packet.getClass().getSimpleName() + " was the wrong length for user \""
|
||||
+ eaglerHandler.getName() + "\" after serialization: " + ret.length + " != " + len);
|
||||
}
|
||||
server.sendData(EaglerBackendRPCProtocol.CHANNEL_NAME, ret);
|
||||
}
|
||||
|
||||
public void handleConnectionLost(ServerInfo server) {
|
||||
if(currentServer != null) {
|
||||
handleDestroyContext();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDestroyContext() {
|
||||
currentServer = null;
|
||||
currentProtocol = null;
|
||||
currentHandler = null;
|
||||
subscribedEvents = 0;
|
||||
}
|
||||
|
||||
private void handleCreateContext(Server server, byte[] data) {
|
||||
EaglerBackendRPCPacket packet;
|
||||
try {
|
||||
packet = decodeRPCPacket(EaglerBackendRPCProtocol.INIT, data);
|
||||
} catch (IOException e) {
|
||||
EaglerXBungee.logger().log(Level.SEVERE, "[" + eaglerHandler.getSocketAddress()
|
||||
+ "]: Recieved invalid backend RPC protocol handshake for user \"" + eaglerHandler.getName() + "\"", e);
|
||||
return;
|
||||
}
|
||||
if(!(packet instanceof CPacketRPCEnabled)) {
|
||||
throw new WrongRPCPacketException();
|
||||
}
|
||||
if(!ArrayUtils.contains(((CPacketRPCEnabled)packet).supportedProtocols, EaglerBackendRPCProtocol.V1.vers)) {
|
||||
EaglerXBungee.logger().severe("[" + eaglerHandler.getSocketAddress()
|
||||
+ "]: Unsupported backend RPC protocol version for user \"" + eaglerHandler.getName() + "\"");
|
||||
sendRPCPacket(EaglerBackendRPCProtocol.INIT, server, new SPacketRPCEnabledFailure(SPacketRPCEnabledFailure.FAILURE_CODE_OUTDATED_SERVER));
|
||||
return;
|
||||
}
|
||||
sendRPCPacket(EaglerBackendRPCProtocol.INIT, server, new SPacketRPCEnabledSuccess(EaglerBackendRPCProtocol.V1.vers, eaglerHandler.getEaglerProtocolHandshake()));
|
||||
currentServer = server;
|
||||
currentProtocol = EaglerBackendRPCProtocol.V1;
|
||||
currentHandler = new ServerV1RPCProtocolHandler(this, server, eaglerHandler);
|
||||
}
|
||||
|
||||
public static void handlePacketOnVanilla(Server server, UserConnection player, byte[] data) {
|
||||
EaglerBackendRPCPacket packet;
|
||||
try {
|
||||
packet = EaglerBackendRPCProtocol.INIT.readPacket(new DataInputStream(new ByteArrayInputStream(data)), EaglerBackendRPCProtocol.CLIENT_TO_SERVER);
|
||||
} catch (IOException e) {
|
||||
EaglerXBungee.logger().log(Level.SEVERE, "[" + player.getSocketAddress()
|
||||
+ "]: Recieved invalid backend RPC protocol handshake for user \"" + player.getName() + "\"", e);
|
||||
EaglerXBungee.logger().severe("(Note: this player is not using Eaglercraft!)");
|
||||
return;
|
||||
}
|
||||
if(!(packet instanceof CPacketRPCEnabled)) {
|
||||
throw new WrongRPCPacketException();
|
||||
}
|
||||
if(!ArrayUtils.contains(((CPacketRPCEnabled)packet).supportedProtocols, EaglerBackendRPCProtocol.V1.vers)) {
|
||||
EaglerXBungee.logger().severe("[" + player.getSocketAddress()
|
||||
+ "]: Unsupported backend RPC protocol version for user \"" + player.getName() + "\"");
|
||||
ByteArrayOutputStream bao = new ByteArrayOutputStream();
|
||||
try {
|
||||
EaglerBackendRPCProtocol.INIT.writePacket(new DataOutputStream(bao),
|
||||
EaglerBackendRPCProtocol.SERVER_TO_CLIENT,
|
||||
new SPacketRPCEnabledFailure(SPacketRPCEnabledFailure.FAILURE_CODE_OUTDATED_SERVER));
|
||||
}catch(IOException ex) {
|
||||
EaglerXBungee.logger().log(Level.SEVERE, "[" + player.getSocketAddress()
|
||||
+ "]: Failed to write backend RPC protocol version for user \"" + player.getName() + "\"", ex);
|
||||
EaglerXBungee.logger().severe("(Note: this player is not using Eaglercraft!)");
|
||||
return;
|
||||
}
|
||||
server.sendData(EaglerBackendRPCProtocol.CHANNEL_NAME, bao.toByteArray());
|
||||
return;
|
||||
}
|
||||
EaglerXBungee.logger().warning("[" + player.getSocketAddress()
|
||||
+ "]: Tried to open backend RPC protocol connection for non-eagler player \"" + player.getName() + "\"");
|
||||
ByteArrayOutputStream bao = new ByteArrayOutputStream();
|
||||
try {
|
||||
EaglerBackendRPCProtocol.INIT.writePacket(new DataOutputStream(bao),
|
||||
EaglerBackendRPCProtocol.SERVER_TO_CLIENT,
|
||||
new SPacketRPCEnabledFailure(SPacketRPCEnabledFailure.FAILURE_CODE_NOT_EAGLER_PLAYER));
|
||||
}catch(IOException ex) {
|
||||
EaglerXBungee.logger().log(Level.SEVERE, "[" + player.getSocketAddress()
|
||||
+ "]: Failed to write backend RPC protocol version for user \"" + player.getName() + "\"", ex);
|
||||
return;
|
||||
}
|
||||
server.sendData(EaglerBackendRPCProtocol.CHANNEL_NAME, bao.toByteArray());
|
||||
}
|
||||
|
||||
public void setSubscribedEvents(int eventsToEnable) {
|
||||
int oldSubscribedEvents = subscribedEvents;
|
||||
subscribedEvents = eventsToEnable;
|
||||
if ((oldSubscribedEvents & CPacketRPCSubscribeEvents.SUBSCRIBE_EVENT_TOGGLE_VOICE) == 0
|
||||
&& (eventsToEnable & CPacketRPCSubscribeEvents.SUBSCRIBE_EVENT_TOGGLE_VOICE) != 0) {
|
||||
currentVoiceState.set(SPacketRPCEventToggledVoice.VOICE_STATE_SERVER_DISABLE);
|
||||
VoiceService svc = EaglerXBungee.getEagler().getVoiceService();
|
||||
if(svc != null && eaglerHandler.getEaglerListenerConfig().getEnableVoiceChat()) {
|
||||
EnumVoiceState state = svc.getPlayerVoiceState(eaglerHandler.getUniqueId(), currentServer.getInfo());
|
||||
if(state == EnumVoiceState.DISABLED) {
|
||||
handleVoiceStateTransition(SPacketRPCEventToggledVoice.VOICE_STATE_DISABLED);
|
||||
}else if(state == EnumVoiceState.ENABLED) {
|
||||
handleVoiceStateTransition(SPacketRPCEventToggledVoice.VOICE_STATE_ENABLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((oldSubscribedEvents & CPacketRPCSubscribeEvents.SUBSCRIBE_EVENT_WEBVIEW_OPEN_CLOSE) == 0
|
||||
&& (eventsToEnable & CPacketRPCSubscribeEvents.SUBSCRIBE_EVENT_WEBVIEW_OPEN_CLOSE) != 0) {
|
||||
if(eaglerHandler.webViewMessageChannelOpen.get()) {
|
||||
sendRPCPacket(new SPacketRPCEventWebViewOpenClose(true, eaglerHandler.webViewMessageChannelName));
|
||||
}
|
||||
}
|
||||
if ((eventsToEnable & ~(CPacketRPCSubscribeEvents.SUBSCRIBE_EVENT_WEBVIEW_OPEN_CLOSE
|
||||
| CPacketRPCSubscribeEvents.SUBSCRIBE_EVENT_WEBVIEW_MESSAGE
|
||||
| CPacketRPCSubscribeEvents.SUBSCRIBE_EVENT_TOGGLE_VOICE)) != 0) {
|
||||
EaglerXBungee.logger()
|
||||
.severe("[" + eaglerHandler.getSocketAddress() + "]: Unsupported events were subscribed to for \""
|
||||
+ eaglerHandler.getName() + "\" via backend RPC protocol, bitfield: " + subscribedEvents);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSubscribed(EnumSubscribedEvent eventType) {
|
||||
switch(eventType) {
|
||||
case WEBVIEW_OPEN_CLOSE:
|
||||
return (subscribedEvents & CPacketRPCSubscribeEvents.SUBSCRIBE_EVENT_WEBVIEW_OPEN_CLOSE) != 0;
|
||||
case WEBVIEW_MESSAGE:
|
||||
return (subscribedEvents & CPacketRPCSubscribeEvents.SUBSCRIBE_EVENT_WEBVIEW_MESSAGE) != 0;
|
||||
case TOGGLE_VOICE:
|
||||
return (subscribedEvents & CPacketRPCSubscribeEvents.SUBSCRIBE_EVENT_TOGGLE_VOICE) != 0;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void handleDisabled() {
|
||||
handleDestroyContext();
|
||||
}
|
||||
|
||||
public void handleVoiceStateTransition(int voiceState) {
|
||||
if((subscribedEvents & CPacketRPCSubscribeEvents.SUBSCRIBE_EVENT_TOGGLE_VOICE) != 0) {
|
||||
int oldState = currentVoiceState.getAndSet(voiceState);
|
||||
if(oldState != voiceState) {
|
||||
sendRPCPacket(new SPacketRPCEventToggledVoice(oldState, voiceState));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.backend_rpc_protocol;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public enum EnumSubscribedEvent {
|
||||
WEBVIEW_OPEN_CLOSE,
|
||||
WEBVIEW_MESSAGE,
|
||||
TOGGLE_VOICE;
|
||||
}
|
@ -0,0 +1,435 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.backend_rpc_protocol;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.RandomAccess;
|
||||
import java.util.UUID;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.EaglerBackendRPCHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.client.*;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.server.*;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.util.PacketImageData;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EnumVoiceState;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EnumWebViewState;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerPauseMenuConfig;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.voice.VoiceService;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketCustomizePauseMenuV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeHideV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeShowV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifIconsRegisterV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifIconsReleaseV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache;
|
||||
import net.md_5.bungee.api.connection.Server;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class ServerV1RPCProtocolHandler implements EaglerBackendRPCHandler {
|
||||
|
||||
protected final BackendRPCSessionHandler sessionHandler;
|
||||
protected final Server server;
|
||||
protected final EaglerInitialHandler eaglerHandler;
|
||||
|
||||
public ServerV1RPCProtocolHandler(BackendRPCSessionHandler sessionHandler, Server server, EaglerInitialHandler eaglerHandler) {
|
||||
this.sessionHandler = sessionHandler;
|
||||
this.server = server;
|
||||
this.eaglerHandler = eaglerHandler;
|
||||
}
|
||||
|
||||
public void handleClient(CPacketRPCRequestPlayerInfo packet) {
|
||||
switch(packet.requestType) {
|
||||
case CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_REAL_UUID: {
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeUUID(packet.requestID, eaglerHandler.getUniqueId()));
|
||||
}
|
||||
break;
|
||||
case CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_REAL_IP: {
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeString(packet.requestID, ((InetSocketAddress)eaglerHandler.getSocketAddress()).getAddress().getHostAddress()));
|
||||
}
|
||||
break;
|
||||
case CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_ORIGIN: {
|
||||
String origin = eaglerHandler.getOrigin();
|
||||
if(origin != null) {
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeString(packet.requestID, origin));
|
||||
}else {
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeNull(packet.requestID));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_USER_AGENT: {
|
||||
String userAgent = eaglerHandler.getUserAgent();
|
||||
if(userAgent != null) {
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeString(packet.requestID, userAgent));
|
||||
}else {
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeNull(packet.requestID));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_SKIN_DATA: {
|
||||
SkinPacketVersionCache skinData = EaglerXBungee.getEagler().getSkinService().getSkin(eaglerHandler.getUniqueId());
|
||||
if(skinData != null) {
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeBytes(packet.requestID, skinData.getV3HandshakeData()));
|
||||
}else {
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeNull(packet.requestID));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CAPE_DATA: {
|
||||
byte[] capeData = EaglerXBungee.getEagler().getCapeService().getCapeHandshakeData(eaglerHandler.getUniqueId());
|
||||
if(capeData != null) {
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeBytes(packet.requestID, capeData));
|
||||
}else {
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeNull(packet.requestID));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_COOKIE: {
|
||||
boolean cookieEnabled = eaglerHandler.getCookieAllowed();
|
||||
byte[] cookieData = cookieEnabled ? eaglerHandler.getCookieData() : null;
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeCookie(packet.requestID, cookieEnabled, cookieData));
|
||||
}
|
||||
break;
|
||||
case CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CLIENT_BRAND_STR: {
|
||||
String clientBrandStr = eaglerHandler.getEaglerBrandString();
|
||||
if(clientBrandStr != null) {
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeString(packet.requestID, clientBrandStr));
|
||||
}else {
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeNull(packet.requestID));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CLIENT_VERSION_STR: {
|
||||
String clientVersionStr = eaglerHandler.getEaglerVersionString();
|
||||
if(clientVersionStr != null) {
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeString(packet.requestID, clientVersionStr));
|
||||
}else {
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeNull(packet.requestID));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CLIENT_BRAND_VERSION_STR: {
|
||||
String clientBrandStr = eaglerHandler.getEaglerBrandString();
|
||||
String clientVersionStr = eaglerHandler.getEaglerVersionString();
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeString(packet.requestID, "" + clientBrandStr + " " + clientVersionStr));
|
||||
}
|
||||
break;
|
||||
case CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CLIENT_BRAND_UUID: {
|
||||
UUID brandUUID = eaglerHandler.getClientBrandUUID();
|
||||
if(brandUUID != null) {
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeUUID(packet.requestID, brandUUID));
|
||||
}else {
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeNull(packet.requestID));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CLIENT_VOICE_STATUS: {
|
||||
int voiceState;
|
||||
VoiceService svc = EaglerXBungee.getEagler().getVoiceService();
|
||||
if(svc != null && eaglerHandler.getEaglerListenerConfig().getEnableVoiceChat()) {
|
||||
EnumVoiceState enumVoiceState = svc.getPlayerVoiceState(eaglerHandler.getUniqueId(), server.getInfo());
|
||||
switch(enumVoiceState) {
|
||||
case SERVER_DISABLE:
|
||||
default:
|
||||
voiceState = SPacketRPCResponseTypeVoiceStatus.VOICE_STATE_SERVER_DISABLE;
|
||||
break;
|
||||
case DISABLED:
|
||||
voiceState = SPacketRPCResponseTypeVoiceStatus.VOICE_STATE_DISABLED;
|
||||
break;
|
||||
case ENABLED:
|
||||
voiceState = SPacketRPCResponseTypeVoiceStatus.VOICE_STATE_ENABLED;
|
||||
break;
|
||||
}
|
||||
}else {
|
||||
voiceState = SPacketRPCResponseTypeVoiceStatus.VOICE_STATE_SERVER_DISABLE;
|
||||
}
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeVoiceStatus(packet.requestID, voiceState));
|
||||
}
|
||||
break;
|
||||
case CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CLIENT_WEBVIEW_STATUS: {
|
||||
EnumWebViewState enumWebViewState = eaglerHandler.getWebViewState();
|
||||
int webViewStatus;
|
||||
String webViewChannel;
|
||||
switch(enumWebViewState) {
|
||||
case NOT_SUPPORTED:
|
||||
default:
|
||||
webViewStatus = SPacketRPCResponseTypeWebViewStatus.WEBVIEW_STATE_NOT_SUPPORTED;
|
||||
webViewChannel = null;
|
||||
break;
|
||||
case SERVER_DISABLE:
|
||||
webViewStatus = SPacketRPCResponseTypeWebViewStatus.WEBVIEW_STATE_SERVER_DISABLE;
|
||||
webViewChannel = null;
|
||||
break;
|
||||
case CHANNEL_CLOSED:
|
||||
webViewStatus = SPacketRPCResponseTypeWebViewStatus.WEBVIEW_STATE_CHANNEL_CLOSED;
|
||||
webViewChannel = null;
|
||||
break;
|
||||
case CHANNEL_OPEN:
|
||||
webViewStatus = SPacketRPCResponseTypeWebViewStatus.WEBVIEW_STATE_CHANNEL_OPEN;
|
||||
webViewChannel = eaglerHandler.getWebViewMessageChannelName();
|
||||
break;
|
||||
}
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeWebViewStatus(packet.requestID, webViewStatus, webViewChannel));
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
sessionHandler.sendRPCPacket(new SPacketRPCResponseTypeError(packet.requestID, "Unknown request type: " + packet.requestType));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClient(CPacketRPCSubscribeEvents packet) {
|
||||
sessionHandler.setSubscribedEvents(packet.eventsToEnable);
|
||||
}
|
||||
|
||||
public void handleClient(CPacketRPCSetPlayerSkin packet) {
|
||||
try {
|
||||
byte[] bs = packet.skinPacket;
|
||||
if(bs.length < 5) {
|
||||
throw new IOException();
|
||||
}
|
||||
if(bs[0] == (byte)1) {
|
||||
if(bs.length != 5) {
|
||||
throw new IOException();
|
||||
}
|
||||
EaglerXBungeeAPIHelper.changePlayerSkinPreset(EaglerXBungeeAPIHelper.getPlayer(eaglerHandler),
|
||||
(bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF), packet.notifyOthers);
|
||||
}else if(bs[0] == (byte)2) {
|
||||
if(bs.length < 2) {
|
||||
throw new IOException();
|
||||
}
|
||||
byte[] cust = new byte[bs.length - 2];
|
||||
System.arraycopy(bs, 2, cust, 0, cust.length);
|
||||
EaglerXBungeeAPIHelper.changePlayerSkinCustom(EaglerXBungeeAPIHelper.getPlayer(eaglerHandler),
|
||||
bs[1] & 0xFF, cust, packet.notifyOthers);
|
||||
}else {
|
||||
throw new IOException();
|
||||
}
|
||||
}catch(IOException ex) {
|
||||
EaglerXBungee.logger().severe("[" + eaglerHandler.getSocketAddress() + "]: Invalid CPacketRPCSetPlayerSkin packet recieved for player \""
|
||||
+ eaglerHandler.getName() + "\" from backend RPC protocol!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClient(CPacketRPCSetPlayerCape packet) {
|
||||
try {
|
||||
byte[] bs = packet.capePacket;
|
||||
if(bs.length < 5) {
|
||||
throw new IOException();
|
||||
}
|
||||
if(bs[0] == (byte)1) {
|
||||
if(bs.length != 5) {
|
||||
throw new IOException();
|
||||
}
|
||||
EaglerXBungeeAPIHelper.changePlayerCapePreset(EaglerXBungeeAPIHelper.getPlayer(eaglerHandler),
|
||||
(bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF), packet.notifyOthers);
|
||||
}else if(bs[0] == (byte)2) {
|
||||
if(bs.length != 1174) {
|
||||
throw new IOException();
|
||||
}
|
||||
byte[] cust = new byte[bs.length - 1];
|
||||
System.arraycopy(bs, 1, cust, 0, cust.length);
|
||||
EaglerXBungeeAPIHelper.changePlayerCapeCustom(EaglerXBungeeAPIHelper.getPlayer(eaglerHandler),
|
||||
cust, true, packet.notifyOthers);
|
||||
}else {
|
||||
throw new IOException();
|
||||
}
|
||||
}catch(IOException ex) {
|
||||
EaglerXBungee.logger().severe("[" + eaglerHandler.getSocketAddress() + "]: Invalid CPacketRPCSetPlayerCape packet recieved for player \""
|
||||
+ eaglerHandler.getName() + "\" from backend RPC protocol!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClient(CPacketRPCSetPlayerCookie packet) {
|
||||
eaglerHandler.setCookieData(packet.cookieData, packet.expires, packet.revokeQuerySupported, packet.saveToDisk);
|
||||
}
|
||||
|
||||
public void handleClient(CPacketRPCSetPlayerFNAWEn packet) {
|
||||
EaglerXBungeeAPIHelper.setEnableForceFNAWSkins(eaglerHandler, packet.enable, packet.force);
|
||||
}
|
||||
|
||||
public void handleClient(CPacketRPCRedirectPlayer packet) {
|
||||
eaglerHandler.redirectPlayerToWebSocket(packet.redirectURI);
|
||||
}
|
||||
|
||||
public void handleClient(CPacketRPCResetPlayerMulti packet) {
|
||||
EaglerXBungeeAPIHelper.resetPlayerMulti(EaglerXBungeeAPIHelper.getPlayer(eaglerHandler), packet.resetSkin,
|
||||
packet.resetCape, packet.resetFNAWForce, packet.notifyOtherPlayers);
|
||||
}
|
||||
|
||||
public void handleClient(CPacketRPCSendWebViewMessage packet) {
|
||||
eaglerHandler.sendWebViewMessage(packet.messageType, packet.messageContent);
|
||||
}
|
||||
|
||||
public void handleClient(CPacketRPCSetPauseMenuCustom packet) {
|
||||
if(eaglerHandler.getEaglerProtocol().ver >= 4) {
|
||||
EaglerPauseMenuConfig defaultConf = EaglerXBungee.getEagler().getConfig().getPauseMenuConf();
|
||||
SPacketCustomizePauseMenuV4EAG defaultPacket = defaultConf.getPacket();
|
||||
int serverInfoMode = packet.serverInfoMode;
|
||||
String serverInfoButtonText;
|
||||
String serverInfoURL;
|
||||
byte[] serverInfoHash;
|
||||
int serverInfoEmbedPerms;
|
||||
String serverInfoEmbedTitle;
|
||||
if(serverInfoMode == CPacketRPCSetPauseMenuCustom.SERVER_INFO_MODE_INHERIT_DEFAULT) {
|
||||
serverInfoMode = defaultPacket.serverInfoMode;
|
||||
serverInfoButtonText = defaultPacket.serverInfoButtonText;
|
||||
serverInfoURL = defaultPacket.serverInfoURL;
|
||||
serverInfoHash = defaultPacket.serverInfoHash;
|
||||
serverInfoEmbedPerms = defaultPacket.serverInfoEmbedPerms;
|
||||
serverInfoEmbedTitle = defaultPacket.serverInfoEmbedTitle;
|
||||
}else {
|
||||
serverInfoButtonText = packet.serverInfoButtonText;
|
||||
serverInfoURL = packet.serverInfoURL;
|
||||
serverInfoHash = packet.serverInfoHash;
|
||||
serverInfoEmbedPerms = packet.serverInfoEmbedPerms;
|
||||
serverInfoEmbedTitle = packet.serverInfoEmbedTitle;
|
||||
}
|
||||
int discordButtonMode = packet.discordButtonMode;
|
||||
String discordButtonText;
|
||||
String discordInviteURL;
|
||||
if(discordButtonMode == CPacketRPCSetPauseMenuCustom.DISCORD_MODE_INHERIT_DEFAULT) {
|
||||
discordButtonMode = defaultPacket.discordButtonMode;
|
||||
discordButtonText = defaultPacket.discordButtonText;
|
||||
discordInviteURL = defaultPacket.discordInviteURL;
|
||||
}else {
|
||||
discordButtonText = packet.discordButtonText;
|
||||
discordInviteURL = packet.discordInviteURL;
|
||||
}
|
||||
Map<String, Integer> imageMappings = packet.imageMappings;
|
||||
List<PacketImageData> imageData = packet.imageData;
|
||||
List<net.lax1dude.eaglercraft.v1_8.socket.protocol.util.PacketImageData> imageDataConv = imageData != null
|
||||
? new ArrayList<>(imageData.size()) : null;
|
||||
if(imageDataConv != null) {
|
||||
for(int i = 0, l = imageData.size(); i < l; ++i) {
|
||||
PacketImageData etr = imageData.get(i);
|
||||
imageDataConv.add(new net.lax1dude.eaglercraft.v1_8.socket.protocol.util.PacketImageData
|
||||
(etr.width, etr.height, etr.rgba));
|
||||
}
|
||||
}
|
||||
eaglerHandler.sendEaglerMessage(new SPacketCustomizePauseMenuV4EAG(serverInfoMode, serverInfoButtonText,
|
||||
serverInfoURL, serverInfoHash, serverInfoEmbedPerms, serverInfoEmbedTitle, discordButtonMode,
|
||||
discordButtonText, discordInviteURL, imageMappings, imageDataConv));
|
||||
}else {
|
||||
EaglerXBungee.logger().warning("[" + eaglerHandler.getSocketAddress()
|
||||
+ "]: Recieved packet CPacketRPCSetPauseMenuCustom for player \"" + eaglerHandler.getName()
|
||||
+ "\" from backend RPC protocol, but their client does not support pause menu customization!");
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClient(CPacketRPCNotifIconRegister packet) {
|
||||
if(eaglerHandler.notificationSupported()) {
|
||||
List<SPacketNotifIconsRegisterV4EAG.CreateIcon> createIconsConv = new ArrayList<>(packet.notifIcons.size());
|
||||
for(Entry<UUID,PacketImageData> etr : packet.notifIcons.entrySet()) {
|
||||
UUID uuid = etr.getKey();
|
||||
PacketImageData imgData = etr.getValue();
|
||||
createIconsConv.add(new SPacketNotifIconsRegisterV4EAG.CreateIcon(uuid.getMostSignificantBits(),
|
||||
uuid.getLeastSignificantBits(), new net.lax1dude.eaglercraft.v1_8.socket.protocol.util.PacketImageData
|
||||
(imgData.width, imgData.height, imgData.rgba)));
|
||||
}
|
||||
eaglerHandler.sendEaglerMessage(new SPacketNotifIconsRegisterV4EAG(createIconsConv));
|
||||
}else {
|
||||
EaglerXBungee.logger().warning("[" + eaglerHandler.getSocketAddress()
|
||||
+ "]: Recieved packet CPacketRPCNotifIconRegister for player \"" + eaglerHandler.getName()
|
||||
+ "\" from backend RPC protocol, but their client does not support notifications!");
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClient(CPacketRPCNotifIconRelease packet) {
|
||||
if(eaglerHandler.notificationSupported()) {
|
||||
List<SPacketNotifIconsReleaseV4EAG.DestroyIcon> destroyIconsConv = new ArrayList<>(packet.iconsToRelease.size());
|
||||
if(packet.iconsToRelease instanceof RandomAccess) {
|
||||
List<UUID> lst = (List<UUID>)packet.iconsToRelease;
|
||||
for(int i = 0, l = lst.size(); i < l; ++i) {
|
||||
UUID uuid = lst.get(i);
|
||||
destroyIconsConv.add(new SPacketNotifIconsReleaseV4EAG.DestroyIcon(uuid.getMostSignificantBits(),
|
||||
uuid.getLeastSignificantBits()));
|
||||
}
|
||||
}else {
|
||||
for(UUID uuid : packet.iconsToRelease) {
|
||||
destroyIconsConv.add(new SPacketNotifIconsReleaseV4EAG.DestroyIcon(uuid.getMostSignificantBits(),
|
||||
uuid.getLeastSignificantBits()));
|
||||
}
|
||||
}
|
||||
eaglerHandler.sendEaglerMessage(new SPacketNotifIconsReleaseV4EAG(destroyIconsConv));
|
||||
}else {
|
||||
EaglerXBungee.logger().warning("[" + eaglerHandler.getSocketAddress()
|
||||
+ "]: Recieved packet CPacketRPCNotifIconRelease for player \"" + eaglerHandler.getName()
|
||||
+ "\" from backend RPC protocol, but their client does not support notifications!");
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClient(CPacketRPCNotifBadgeShow packet) {
|
||||
if(eaglerHandler.notificationSupported()) {
|
||||
SPacketNotifBadgeShowV4EAG.EnumBadgePriority translatedEnum;
|
||||
switch(packet.priority) {
|
||||
case LOW:
|
||||
default:
|
||||
translatedEnum = SPacketNotifBadgeShowV4EAG.EnumBadgePriority.LOW;
|
||||
break;
|
||||
case NORMAL:
|
||||
translatedEnum = SPacketNotifBadgeShowV4EAG.EnumBadgePriority.NORMAL;
|
||||
break;
|
||||
case HIGHER:
|
||||
translatedEnum = SPacketNotifBadgeShowV4EAG.EnumBadgePriority.HIGHER;
|
||||
break;
|
||||
case HIGHEST:
|
||||
translatedEnum = SPacketNotifBadgeShowV4EAG.EnumBadgePriority.HIGHEST;
|
||||
break;
|
||||
}
|
||||
eaglerHandler.sendEaglerMessage(new SPacketNotifBadgeShowV4EAG(packet.badgeUUID.getMostSignificantBits(),
|
||||
packet.badgeUUID.getLeastSignificantBits(), packet.bodyComponent, packet.titleComponent,
|
||||
packet.sourceComponent, packet.originalTimestampSec, packet.silent, translatedEnum,
|
||||
(packet.mainIconUUID != null ? packet.mainIconUUID.getMostSignificantBits() : 0l),
|
||||
(packet.mainIconUUID != null ? packet.mainIconUUID.getLeastSignificantBits() : 0l),
|
||||
(packet.titleIconUUID != null ? packet.titleIconUUID.getMostSignificantBits() : 0l),
|
||||
(packet.titleIconUUID != null ? packet.titleIconUUID.getLeastSignificantBits() : 0l),
|
||||
packet.hideAfterSec, packet.expireAfterSec, packet.backgroundColor, packet.bodyTxtColor,
|
||||
packet.titleTxtColor, packet.sourceTxtColor));
|
||||
}else {
|
||||
EaglerXBungee.logger().warning("[" + eaglerHandler.getSocketAddress()
|
||||
+ "]: Recieved packet CPacketRPCNotifBadgeShow for player \"" + eaglerHandler.getName()
|
||||
+ "\" from backend RPC protocol, but their client does not support notifications!");
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClient(CPacketRPCNotifBadgeHide packet) {
|
||||
if(eaglerHandler.notificationSupported()) {
|
||||
eaglerHandler.sendEaglerMessage(new SPacketNotifBadgeHideV4EAG(packet.badgeUUID.getMostSignificantBits(),
|
||||
packet.badgeUUID.getLeastSignificantBits()));
|
||||
}else {
|
||||
EaglerXBungee.logger().warning("[" + eaglerHandler.getSocketAddress()
|
||||
+ "]: Recieved packet CPacketRPCNotifBadgeHide for player \"" + eaglerHandler.getName()
|
||||
+ "\" from backend RPC protocol, but their client does not support notifications!");
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClient(CPacketRPCDisabled packet) {
|
||||
sessionHandler.handleDisabled();
|
||||
}
|
||||
|
||||
public void handleClient(CPacketRPCSendRawMessage packet) {
|
||||
eaglerHandler.getEaglerMessageController().getUserConnection().sendData(packet.messageChannel, packet.messageData);
|
||||
}
|
||||
|
||||
}
|
@ -1,254 +0,0 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.bungeeprotocol;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Iterables;
|
||||
import gnu.trove.map.TIntObjectMap;
|
||||
import gnu.trove.map.TObjectIntMap;
|
||||
import gnu.trove.map.hash.TIntObjectHashMap;
|
||||
import gnu.trove.map.hash.TObjectIntHashMap;
|
||||
import net.md_5.bungee.protocol.BadPacketException;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.ProtocolConstants;
|
||||
import net.md_5.bungee.protocol.packet.*;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* The original net.md_5.bungee.protocol.Protocol is inaccessible due to java
|
||||
* security rules
|
||||
*/
|
||||
|
||||
public enum EaglerBungeeProtocol {
|
||||
|
||||
// Undef
|
||||
HANDSHAKE {
|
||||
|
||||
{
|
||||
TO_SERVER.registerPacket(Handshake.class, Handshake::new, map(ProtocolConstants.MINECRAFT_1_8, 0x00));
|
||||
}
|
||||
},
|
||||
// 0
|
||||
GAME {
|
||||
|
||||
{
|
||||
TO_CLIENT.registerPacket(KeepAlive.class, KeepAlive::new, map(ProtocolConstants.MINECRAFT_1_8, 0x00));
|
||||
TO_CLIENT.registerPacket(Login.class, Login::new, map(ProtocolConstants.MINECRAFT_1_8, 0x01));
|
||||
TO_CLIENT.registerPacket(Chat.class, Chat::new, map(ProtocolConstants.MINECRAFT_1_8, 0x02));
|
||||
TO_CLIENT.registerPacket(Respawn.class, Respawn::new, map(ProtocolConstants.MINECRAFT_1_8, 0x07));
|
||||
TO_CLIENT.registerPacket(PlayerListItem.class, // PlayerInfo
|
||||
PlayerListItem::new, map(ProtocolConstants.MINECRAFT_1_8, 0x38));
|
||||
TO_CLIENT.registerPacket(TabCompleteResponse.class, TabCompleteResponse::new,
|
||||
map(ProtocolConstants.MINECRAFT_1_8, 0x3A));
|
||||
TO_CLIENT.registerPacket(ScoreboardObjective.class, ScoreboardObjective::new,
|
||||
map(ProtocolConstants.MINECRAFT_1_8, 0x3B));
|
||||
TO_CLIENT.registerPacket(ScoreboardScore.class, ScoreboardScore::new,
|
||||
map(ProtocolConstants.MINECRAFT_1_8, 0x3C));
|
||||
TO_CLIENT.registerPacket(ScoreboardDisplay.class, ScoreboardDisplay::new,
|
||||
map(ProtocolConstants.MINECRAFT_1_8, 0x3D));
|
||||
TO_CLIENT.registerPacket(Team.class, Team::new, map(ProtocolConstants.MINECRAFT_1_8, 0x3E));
|
||||
TO_CLIENT.registerPacket(PluginMessage.class, PluginMessage::new,
|
||||
map(ProtocolConstants.MINECRAFT_1_8, 0x3F));
|
||||
TO_CLIENT.registerPacket(Kick.class, Kick::new, map(ProtocolConstants.MINECRAFT_1_8, 0x40));
|
||||
TO_CLIENT.registerPacket(Title.class, Title::new, map(ProtocolConstants.MINECRAFT_1_8, 0x45));
|
||||
TO_CLIENT.registerPacket(PlayerListHeaderFooter.class, PlayerListHeaderFooter::new,
|
||||
map(ProtocolConstants.MINECRAFT_1_8, 0x47));
|
||||
TO_CLIENT.registerPacket(EntityStatus.class, EntityStatus::new, map(ProtocolConstants.MINECRAFT_1_8, 0x1A));
|
||||
|
||||
TO_SERVER.registerPacket(KeepAlive.class, KeepAlive::new, map(ProtocolConstants.MINECRAFT_1_8, 0x00));
|
||||
TO_SERVER.registerPacket(Chat.class, Chat::new, map(ProtocolConstants.MINECRAFT_1_8, 0x01));
|
||||
TO_SERVER.registerPacket(TabCompleteRequest.class, TabCompleteRequest::new,
|
||||
map(ProtocolConstants.MINECRAFT_1_8, 0x14));
|
||||
TO_SERVER.registerPacket(ClientSettings.class, ClientSettings::new,
|
||||
map(ProtocolConstants.MINECRAFT_1_8, 0x15));
|
||||
TO_SERVER.registerPacket(PluginMessage.class, PluginMessage::new,
|
||||
map(ProtocolConstants.MINECRAFT_1_8, 0x17));
|
||||
}
|
||||
},
|
||||
// 1
|
||||
STATUS {
|
||||
|
||||
{
|
||||
TO_CLIENT.registerPacket(StatusResponse.class, StatusResponse::new,
|
||||
map(ProtocolConstants.MINECRAFT_1_8, 0x00));
|
||||
TO_CLIENT.registerPacket(PingPacket.class, PingPacket::new, map(ProtocolConstants.MINECRAFT_1_8, 0x01));
|
||||
|
||||
TO_SERVER.registerPacket(StatusRequest.class, StatusRequest::new,
|
||||
map(ProtocolConstants.MINECRAFT_1_8, 0x00));
|
||||
TO_SERVER.registerPacket(PingPacket.class, PingPacket::new, map(ProtocolConstants.MINECRAFT_1_8, 0x01));
|
||||
}
|
||||
},
|
||||
// 2
|
||||
LOGIN {
|
||||
|
||||
{
|
||||
TO_CLIENT.registerPacket(Kick.class, Kick::new, map(ProtocolConstants.MINECRAFT_1_8, 0x00));
|
||||
TO_CLIENT.registerPacket(EncryptionRequest.class, EncryptionRequest::new,
|
||||
map(ProtocolConstants.MINECRAFT_1_8, 0x01));
|
||||
TO_CLIENT.registerPacket(LoginSuccess.class, LoginSuccess::new, map(ProtocolConstants.MINECRAFT_1_8, 0x02));
|
||||
TO_CLIENT.registerPacket(SetCompression.class, SetCompression::new,
|
||||
map(ProtocolConstants.MINECRAFT_1_8, 0x03));
|
||||
|
||||
TO_SERVER.registerPacket(LoginRequest.class, LoginRequest::new, map(ProtocolConstants.MINECRAFT_1_8, 0x00));
|
||||
TO_SERVER.registerPacket(EncryptionResponse.class, EncryptionResponse::new,
|
||||
map(ProtocolConstants.MINECRAFT_1_8, 0x01));
|
||||
}
|
||||
},
|
||||
// 3
|
||||
CONFIGURATION {
|
||||
|
||||
};
|
||||
|
||||
/* ======================================================================== */
|
||||
public static final int MAX_PACKET_ID = 0xFF;
|
||||
/* ======================================================================== */
|
||||
public final DirectionData TO_SERVER = new DirectionData(this, ProtocolConstants.Direction.TO_SERVER);
|
||||
public final DirectionData TO_CLIENT = new DirectionData(this, ProtocolConstants.Direction.TO_CLIENT);
|
||||
|
||||
public static void main(String[] args) {
|
||||
for (int version : ProtocolConstants.SUPPORTED_VERSION_IDS) {
|
||||
dump(version);
|
||||
}
|
||||
}
|
||||
|
||||
private static void dump(int version) {
|
||||
for (EaglerBungeeProtocol protocol : EaglerBungeeProtocol.values()) {
|
||||
dump(version, protocol);
|
||||
}
|
||||
}
|
||||
|
||||
private static void dump(int version, EaglerBungeeProtocol protocol) {
|
||||
dump(version, protocol.TO_CLIENT);
|
||||
dump(version, protocol.TO_SERVER);
|
||||
}
|
||||
|
||||
private static void dump(int version, DirectionData data) {
|
||||
for (int id = 0; id < MAX_PACKET_ID; id++) {
|
||||
DefinedPacket packet = data.createPacket(id, version);
|
||||
if (packet != null) {
|
||||
System.out.println(version + " " + data.protocolPhase + " " + data.direction + " " + id + " "
|
||||
+ packet.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ProtocolData {
|
||||
|
||||
private final int protocolVersion;
|
||||
private final TObjectIntMap<Class<? extends DefinedPacket>> packetMap = new TObjectIntHashMap<>(MAX_PACKET_ID);
|
||||
@SuppressWarnings("unchecked")
|
||||
private final Supplier<? extends DefinedPacket>[] packetConstructors = new Supplier[MAX_PACKET_ID];
|
||||
|
||||
private ProtocolData(int protocolVersion) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ProtocolMapping {
|
||||
|
||||
private final int protocolVersion;
|
||||
private final int packetID;
|
||||
|
||||
private ProtocolMapping(int protocolVersion, int packetID) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
this.packetID = packetID;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper method
|
||||
private static ProtocolMapping map(int protocol, int id) {
|
||||
return new ProtocolMapping(protocol, id);
|
||||
}
|
||||
|
||||
public static final class DirectionData {
|
||||
|
||||
private final TIntObjectMap<ProtocolData> protocols = new TIntObjectHashMap<>();
|
||||
//
|
||||
private final EaglerBungeeProtocol protocolPhase;
|
||||
private final ProtocolConstants.Direction direction;
|
||||
|
||||
public DirectionData(EaglerBungeeProtocol protocolPhase, ProtocolConstants.Direction direction) {
|
||||
this.protocolPhase = protocolPhase;
|
||||
this.direction = direction;
|
||||
|
||||
for (int protocol : ProtocolConstants.SUPPORTED_VERSION_IDS) {
|
||||
protocols.put(protocol, new ProtocolData(protocol));
|
||||
}
|
||||
}
|
||||
|
||||
private ProtocolData getProtocolData(int version) {
|
||||
ProtocolData protocol = protocols.get(version);
|
||||
if (protocol == null && (protocolPhase != EaglerBungeeProtocol.GAME)) {
|
||||
protocol = Iterables.getFirst(protocols.valueCollection(), null);
|
||||
}
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public final DefinedPacket createPacket(int id, int version) {
|
||||
ProtocolData protocolData = getProtocolData(version);
|
||||
if (protocolData == null) {
|
||||
throw new BadPacketException("Unsupported protocol version " + version);
|
||||
}
|
||||
if (id > MAX_PACKET_ID || id < 0) {
|
||||
throw new BadPacketException("Packet with id " + id + " outside of range");
|
||||
}
|
||||
|
||||
Supplier<? extends DefinedPacket> constructor = protocolData.packetConstructors[id];
|
||||
return (constructor == null) ? null : constructor.get();
|
||||
}
|
||||
|
||||
private void registerPacket(Class<? extends DefinedPacket> packetClass,
|
||||
Supplier<? extends DefinedPacket> constructor, ProtocolMapping... mappings) {
|
||||
int mappingIndex = 0;
|
||||
ProtocolMapping mapping = mappings[mappingIndex];
|
||||
for (int protocol : ProtocolConstants.SUPPORTED_VERSION_IDS) {
|
||||
if (protocol < mapping.protocolVersion) {
|
||||
// This is a new packet, skip it till we reach the next protocol
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mapping.protocolVersion < protocol && mappingIndex + 1 < mappings.length) {
|
||||
// Mapping is non current, but the next one may be ok
|
||||
ProtocolMapping nextMapping = mappings[mappingIndex + 1];
|
||||
|
||||
if (nextMapping.protocolVersion == protocol) {
|
||||
Preconditions.checkState(nextMapping.packetID != mapping.packetID,
|
||||
"Duplicate packet mapping (%s, %s)", mapping.protocolVersion,
|
||||
nextMapping.protocolVersion);
|
||||
|
||||
mapping = nextMapping;
|
||||
mappingIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
if (mapping.packetID < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
ProtocolData data = protocols.get(protocol);
|
||||
data.packetMap.put(packetClass, mapping.packetID);
|
||||
data.packetConstructors[mapping.packetID] = constructor;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasPacket(Class<? extends DefinedPacket> packet, int version) {
|
||||
ProtocolData protocolData = getProtocolData(version);
|
||||
if (protocolData == null) {
|
||||
throw new BadPacketException("Unsupported protocol version");
|
||||
}
|
||||
|
||||
return protocolData.packetMap.containsKey(packet);
|
||||
}
|
||||
|
||||
final int getId(Class<? extends DefinedPacket> packet, int version) {
|
||||
|
||||
ProtocolData protocolData = getProtocolData(version);
|
||||
if (protocolData == null) {
|
||||
throw new BadPacketException("Unsupported protocol version");
|
||||
}
|
||||
Preconditions.checkArgument(protocolData.packetMap.containsKey(packet),
|
||||
"Cannot get ID for packet %s in phase %s with direction %s", packet, protocolPhase, direction);
|
||||
|
||||
return protocolData.packetMap.get(packet);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,291 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.protocol;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.util.concurrent.ScheduledFuture;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageConstants;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.ReusableByteArrayInputStream;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.ReusableByteArrayOutputStream;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SimpleInputBufferImpl;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SimpleOutputBufferImpl;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class GameProtocolMessageController {
|
||||
|
||||
public final GamePluginMessageProtocol protocol;
|
||||
public final GameMessageHandler handler;
|
||||
private final ReusableByteArrayInputStream byteInputStreamSingleton = new ReusableByteArrayInputStream();
|
||||
private final ReusableByteArrayOutputStream byteOutputStreamSingleton = new ReusableByteArrayOutputStream();
|
||||
private final SimpleInputBufferImpl inputStreamSingleton = new SimpleInputBufferImpl(byteInputStreamSingleton);
|
||||
private final SimpleOutputBufferImpl outputStreamSingleton = new SimpleOutputBufferImpl(byteOutputStreamSingleton);
|
||||
private final ReentrantLock inputStreamLock = new ReentrantLock();
|
||||
private final ReentrantLock outputStreamLock = new ReentrantLock();
|
||||
private final UserConnection owner;
|
||||
private int defagSendDelay;
|
||||
|
||||
private final List<byte[]> sendQueueV4 = new LinkedList<>();
|
||||
private volatile int sendQueueByteLengthV4 = 0;
|
||||
private volatile Callable<Void> futureSendCallableV4 = null;
|
||||
private volatile ScheduledFuture<Void> futureSendTaskV4 = null;
|
||||
|
||||
public GameProtocolMessageController(UserConnection owner, GamePluginMessageProtocol protocol,
|
||||
GameMessageHandler handler, int defagSendDelay) {
|
||||
this.owner = owner;
|
||||
this.protocol = protocol;
|
||||
this.handler = handler;
|
||||
this.defagSendDelay = defagSendDelay;
|
||||
}
|
||||
|
||||
public boolean handlePacket(String channel, byte[] data) throws IOException {
|
||||
GameMessagePacket pkt;
|
||||
if(inputStreamLock.tryLock()) {
|
||||
try {
|
||||
byteInputStreamSingleton.feedBuffer(data);
|
||||
if(protocol.ver >= 4 && data.length > 0 && data[0] == (byte)0xFF && channel.equals(GamePluginMessageConstants.V4_CHANNEL)) {
|
||||
inputStreamSingleton.readByte();
|
||||
int count = inputStreamSingleton.readVarInt();
|
||||
for(int i = 0, j, k; i < count; ++i) {
|
||||
j = inputStreamSingleton.readVarInt();
|
||||
k = byteInputStreamSingleton.getReaderIndex() + j;
|
||||
if(j > inputStreamSingleton.available()) {
|
||||
throw new IOException("Packet fragment is too long: " + j + " > " + inputStreamSingleton.available());
|
||||
}
|
||||
pkt = protocol.readPacket(channel, GamePluginMessageConstants.CLIENT_TO_SERVER, inputStreamSingleton);
|
||||
if(pkt != null) {
|
||||
pkt.handlePacket(handler);
|
||||
}else {
|
||||
throw new IOException("Unknown packet type in fragment!");
|
||||
}
|
||||
if(byteInputStreamSingleton.getReaderIndex() != k) {
|
||||
throw new IOException("Packet fragment was the wrong length: " + (j + byteInputStreamSingleton.getReaderIndex() - k) + " != " + j);
|
||||
}
|
||||
}
|
||||
if(inputStreamSingleton.available() > 0) {
|
||||
throw new IOException("Leftover data after reading multi-packet! (" + inputStreamSingleton.available() + " bytes)");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
inputStreamSingleton.setToByteArrayReturns(data);
|
||||
pkt = protocol.readPacket(channel, GamePluginMessageConstants.CLIENT_TO_SERVER, inputStreamSingleton);
|
||||
if(pkt != null && byteInputStreamSingleton.available() != 0) {
|
||||
throw new IOException("Packet was the wrong length: " + pkt.getClass().getSimpleName());
|
||||
}
|
||||
}finally {
|
||||
byteInputStreamSingleton.feedBuffer(null);
|
||||
inputStreamSingleton.setToByteArrayReturns(null);
|
||||
inputStreamLock.unlock();
|
||||
}
|
||||
}else {
|
||||
// slow version that makes multiple new objects
|
||||
ReusableByteArrayInputStream inputStream = new ReusableByteArrayInputStream();
|
||||
inputStream.feedBuffer(data);
|
||||
SimpleInputBufferImpl inputBuffer = new SimpleInputBufferImpl(inputStream, data);
|
||||
if(protocol.ver >= 4 && channel.equals(GamePluginMessageConstants.V4_CHANNEL)) {
|
||||
inputBuffer.setToByteArrayReturns(null);
|
||||
inputBuffer.readByte();
|
||||
int count = inputBuffer.readVarInt();
|
||||
for(int i = 0, j, k; i < count; ++i) {
|
||||
j = inputBuffer.readVarInt();
|
||||
k = inputStream.getReaderIndex() + j;
|
||||
if(j > inputBuffer.available()) {
|
||||
throw new IOException("Packet fragment is too long: " + j + " > " + inputBuffer.available());
|
||||
}
|
||||
pkt = protocol.readPacket(channel, GamePluginMessageConstants.CLIENT_TO_SERVER, inputBuffer);
|
||||
if(pkt != null) {
|
||||
pkt.handlePacket(handler);
|
||||
}else {
|
||||
throw new IOException("Unknown packet type in fragment!");
|
||||
}
|
||||
if(inputStream.getReaderIndex() != k) {
|
||||
throw new IOException("Packet fragment was the wrong length: " + (j + inputStream.getReaderIndex() - k) + " != " + j);
|
||||
}
|
||||
}
|
||||
if(inputBuffer.available() > 0) {
|
||||
throw new IOException("Leftover data after reading multi-packet! (" + inputBuffer.available() + " bytes)");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
pkt = protocol.readPacket(channel, GamePluginMessageConstants.CLIENT_TO_SERVER, inputBuffer);
|
||||
if(pkt != null && inputStream.available() != 0) {
|
||||
throw new IOException("Packet was the wrong length: " + pkt.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
if(pkt != null) {
|
||||
pkt.handlePacket(handler);
|
||||
return true;
|
||||
}else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPacketImmediately(GameMessagePacket packet) throws IOException {
|
||||
sendPacket(packet, true);
|
||||
}
|
||||
|
||||
public void sendPacket(GameMessagePacket packet) throws IOException {
|
||||
sendPacket(packet, false);
|
||||
}
|
||||
|
||||
protected void sendPacket(GameMessagePacket packet, boolean immediately) throws IOException {
|
||||
int len = packet.length() + 1;
|
||||
String chan;
|
||||
byte[] data;
|
||||
if(outputStreamLock.tryLock()) {
|
||||
try {
|
||||
byteOutputStreamSingleton.feedBuffer(new byte[len == 0 ? 64 : len]);
|
||||
chan = protocol.writePacket(GamePluginMessageConstants.SERVER_TO_CLIENT, outputStreamSingleton, packet);
|
||||
data = byteOutputStreamSingleton.returnBuffer();
|
||||
}finally {
|
||||
byteOutputStreamSingleton.feedBuffer(null);
|
||||
outputStreamLock.unlock();
|
||||
}
|
||||
}else {
|
||||
// slow version that makes multiple new objects
|
||||
ReusableByteArrayOutputStream bao = new ReusableByteArrayOutputStream();
|
||||
bao.feedBuffer(new byte[len == 0 ? 64 : len]);
|
||||
SimpleOutputBufferImpl outputStream = new SimpleOutputBufferImpl(bao);
|
||||
chan = protocol.writePacket(GamePluginMessageConstants.SERVER_TO_CLIENT, outputStream, packet);
|
||||
data = bao.returnBuffer();
|
||||
}
|
||||
if(len != 0 && data.length != len && data.length + 1 != len) {
|
||||
EaglerXBungee.logger().warning("Packet " + packet.getClass().getSimpleName()
|
||||
+ " was the wrong length after serialization, " + data.length + " != " + len);
|
||||
}
|
||||
if(defagSendDelay > 0 && protocol.ver >= 4 && chan.equals(GamePluginMessageConstants.V4_CHANNEL)) {
|
||||
synchronized(sendQueueV4) {
|
||||
int varIntLen = GamePacketOutputBuffer.getVarIntSize(data.length);
|
||||
if(immediately || sendQueueByteLengthV4 + data.length + varIntLen > 32760) {
|
||||
if(futureSendTaskV4 != null && !futureSendTaskV4.isDone()) {
|
||||
futureSendTaskV4.cancel(false);
|
||||
futureSendTaskV4 = null;
|
||||
futureSendCallableV4 = null;
|
||||
}
|
||||
if(!sendQueueV4.isEmpty()) {
|
||||
flushQueue();
|
||||
}
|
||||
}
|
||||
if(immediately) {
|
||||
owner.sendData(chan, data);
|
||||
}else {
|
||||
sendQueueV4.add(data);
|
||||
if(futureSendTaskV4 == null || futureSendTaskV4.isDone()) {
|
||||
futureSendTaskV4 = ((EaglerInitialHandler) owner.getPendingConnection()).ch.getHandle()
|
||||
.eventLoop().schedule(futureSendCallableV4 = new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
synchronized (sendQueueV4) {
|
||||
if (futureSendCallableV4 != this) {
|
||||
return null;
|
||||
}
|
||||
futureSendTaskV4 = null;
|
||||
futureSendCallableV4 = null;
|
||||
if(!sendQueueV4.isEmpty()) {
|
||||
flushQueue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, defagSendDelay, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}else {
|
||||
owner.sendData(chan, data);
|
||||
}
|
||||
}
|
||||
|
||||
private void flushQueue() {
|
||||
if(!owner.isConnected()) {
|
||||
sendQueueV4.clear();
|
||||
return;
|
||||
}
|
||||
byte[] pkt;
|
||||
if(sendQueueV4.size() == 1) {
|
||||
pkt = sendQueueV4.remove(0);
|
||||
owner.sendData(GamePluginMessageConstants.V4_CHANNEL, pkt);
|
||||
}else {
|
||||
int i, j, sendCount = 0, totalLen = 0;
|
||||
while(!sendQueueV4.isEmpty()) {
|
||||
do {
|
||||
i = sendQueueV4.get(sendCount++).length;
|
||||
totalLen += GamePacketOutputBuffer.getVarIntSize(i) + i;
|
||||
}while(totalLen < 32760 && sendCount < sendQueueV4.size());
|
||||
if(totalLen >= 32760) {
|
||||
--sendCount;
|
||||
}
|
||||
if(sendCount <= 1) {
|
||||
pkt = sendQueueV4.remove(0);
|
||||
owner.sendData(GamePluginMessageConstants.V4_CHANNEL, pkt);
|
||||
continue;
|
||||
}
|
||||
byte[] toSend = new byte[1 + totalLen + GamePacketOutputBuffer.getVarIntSize(sendCount)];
|
||||
ByteBuf sendBuffer = Unpooled.wrappedBuffer(toSend);
|
||||
sendBuffer.writerIndex(0);
|
||||
sendBuffer.writeByte(0xFF);
|
||||
DefinedPacket.writeVarInt(sendCount, sendBuffer);
|
||||
for(j = 0; j < sendCount; ++j) {
|
||||
pkt = sendQueueV4.remove(0);
|
||||
DefinedPacket.writeVarInt(pkt.length, sendBuffer);
|
||||
sendBuffer.writeBytes(pkt);
|
||||
}
|
||||
owner.sendData(GamePluginMessageConstants.V4_CHANNEL, toSend);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static GameMessageHandler createServerHandler(int protocolVersion, UserConnection conn, EaglerXBungee plugin) {
|
||||
switch(protocolVersion) {
|
||||
case 2:
|
||||
case 3:
|
||||
return new ServerV3MessageHandler(conn, plugin);
|
||||
case 4:
|
||||
return new ServerV4MessageHandler(conn, plugin);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown protocol verison: " + protocolVersion);
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendHelper(UserConnection conn, GameMessagePacket packet) {
|
||||
PendingConnection p = conn.getPendingConnection();
|
||||
if(p instanceof EaglerInitialHandler) {
|
||||
((EaglerInitialHandler)p).sendEaglerMessage(packet);
|
||||
}else {
|
||||
throw new UnsupportedOperationException("Tried to send eagler packet on a non-eagler connection!");
|
||||
}
|
||||
}
|
||||
|
||||
public UserConnection getUserConnection() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.protocol;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.voice.VoiceService;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.*;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class ServerV3MessageHandler implements GameMessageHandler {
|
||||
|
||||
private final UserConnection conn;
|
||||
private final EaglerXBungee plugin;
|
||||
|
||||
public ServerV3MessageHandler(UserConnection conn, EaglerXBungee plugin) {
|
||||
this.conn = conn;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void handleClient(CPacketGetOtherCapeEAG packet) {
|
||||
plugin.getCapeService().processGetOtherCape(new UUID(packet.uuidMost, packet.uuidLeast), conn);
|
||||
}
|
||||
|
||||
public void handleClient(CPacketGetOtherSkinEAG packet) {
|
||||
plugin.getSkinService().processGetOtherSkin(new UUID(packet.uuidMost, packet.uuidLeast), conn);
|
||||
}
|
||||
|
||||
public void handleClient(CPacketGetSkinByURLEAG packet) {
|
||||
plugin.getSkinService().processGetOtherSkin(new UUID(packet.uuidMost, packet.uuidLeast), packet.url, conn);
|
||||
}
|
||||
|
||||
public void handleClient(CPacketVoiceSignalConnectEAG packet) {
|
||||
VoiceService svc = plugin.getVoiceService();
|
||||
if(svc != null && ((EaglerInitialHandler)conn.getPendingConnection()).getEaglerListenerConfig().getEnableVoiceChat()) {
|
||||
svc.handleVoiceSignalPacketTypeConnect(conn);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClient(CPacketVoiceSignalDescEAG packet) {
|
||||
VoiceService svc = plugin.getVoiceService();
|
||||
if(svc != null && ((EaglerInitialHandler)conn.getPendingConnection()).getEaglerListenerConfig().getEnableVoiceChat()) {
|
||||
svc.handleVoiceSignalPacketTypeDesc(new UUID(packet.uuidMost, packet.uuidLeast), packet.desc, conn);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClient(CPacketVoiceSignalDisconnectV3EAG packet) {
|
||||
VoiceService svc = plugin.getVoiceService();
|
||||
if(svc != null && ((EaglerInitialHandler)conn.getPendingConnection()).getEaglerListenerConfig().getEnableVoiceChat()) {
|
||||
if(packet.isPeerType) {
|
||||
svc.handleVoiceSignalPacketTypeDisconnectPeer(new UUID(packet.uuidMost, packet.uuidLeast), conn);
|
||||
}else {
|
||||
svc.handleVoiceSignalPacketTypeDisconnect(conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClient(CPacketVoiceSignalICEEAG packet) {
|
||||
VoiceService svc = plugin.getVoiceService();
|
||||
if(svc != null && ((EaglerInitialHandler)conn.getPendingConnection()).getEaglerListenerConfig().getEnableVoiceChat()) {
|
||||
svc.handleVoiceSignalPacketTypeICE(new UUID(packet.uuidMost, packet.uuidLeast), packet.ice, conn);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClient(CPacketVoiceSignalRequestEAG packet) {
|
||||
VoiceService svc = plugin.getVoiceService();
|
||||
if(svc != null && ((EaglerInitialHandler)conn.getPendingConnection()).getEaglerListenerConfig().getEnableVoiceChat()) {
|
||||
svc.handleVoiceSignalPacketTypeRequest(new UUID(packet.uuidMost, packet.uuidLeast), conn);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,182 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.protocol;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.server.SPacketRPCEventWebViewMessage;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.server.SPacketRPCEventWebViewOpenClose;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftWebViewChannelEvent;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftWebViewMessageEvent;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerPauseMenuConfig;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.backend_rpc_protocol.EnumSubscribedEvent;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.voice.VoiceService;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.*;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class ServerV4MessageHandler implements GameMessageHandler {
|
||||
|
||||
private final UserConnection conn;
|
||||
private final EaglerXBungee plugin;
|
||||
|
||||
public ServerV4MessageHandler(UserConnection conn, EaglerXBungee plugin) {
|
||||
this.conn = conn;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void handleClient(CPacketGetOtherCapeEAG packet) {
|
||||
plugin.getCapeService().processGetOtherCape(new UUID(packet.uuidMost, packet.uuidLeast), conn);
|
||||
}
|
||||
|
||||
public void handleClient(CPacketGetOtherSkinEAG packet) {
|
||||
plugin.getSkinService().processGetOtherSkin(new UUID(packet.uuidMost, packet.uuidLeast), conn);
|
||||
}
|
||||
|
||||
public void handleClient(CPacketGetSkinByURLEAG packet) {
|
||||
plugin.getSkinService().processGetOtherSkin(new UUID(packet.uuidMost, packet.uuidLeast), packet.url, conn);
|
||||
}
|
||||
|
||||
public void handleClient(CPacketVoiceSignalConnectEAG packet) {
|
||||
VoiceService svc = plugin.getVoiceService();
|
||||
if(svc != null && ((EaglerInitialHandler)conn.getPendingConnection()).getEaglerListenerConfig().getEnableVoiceChat()) {
|
||||
svc.handleVoiceSignalPacketTypeConnect(conn);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClient(CPacketVoiceSignalDescEAG packet) {
|
||||
VoiceService svc = plugin.getVoiceService();
|
||||
if(svc != null && ((EaglerInitialHandler)conn.getPendingConnection()).getEaglerListenerConfig().getEnableVoiceChat()) {
|
||||
svc.handleVoiceSignalPacketTypeDesc(new UUID(packet.uuidMost, packet.uuidLeast), packet.desc, conn);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClient(CPacketVoiceSignalDisconnectV4EAG packet) {
|
||||
VoiceService svc = plugin.getVoiceService();
|
||||
if(svc != null && ((EaglerInitialHandler)conn.getPendingConnection()).getEaglerListenerConfig().getEnableVoiceChat()) {
|
||||
svc.handleVoiceSignalPacketTypeDisconnect(conn);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClient(CPacketVoiceSignalDisconnectPeerV4EAG packet) {
|
||||
VoiceService svc = plugin.getVoiceService();
|
||||
if(svc != null && ((EaglerInitialHandler)conn.getPendingConnection()).getEaglerListenerConfig().getEnableVoiceChat()) {
|
||||
svc.handleVoiceSignalPacketTypeDisconnectPeer(new UUID(packet.uuidMost, packet.uuidLeast), conn);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClient(CPacketVoiceSignalICEEAG packet) {
|
||||
VoiceService svc = plugin.getVoiceService();
|
||||
if(svc != null && ((EaglerInitialHandler)conn.getPendingConnection()).getEaglerListenerConfig().getEnableVoiceChat()) {
|
||||
svc.handleVoiceSignalPacketTypeICE(new UUID(packet.uuidMost, packet.uuidLeast), packet.ice, conn);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClient(CPacketVoiceSignalRequestEAG packet) {
|
||||
VoiceService svc = plugin.getVoiceService();
|
||||
if(svc != null && ((EaglerInitialHandler)conn.getPendingConnection()).getEaglerListenerConfig().getEnableVoiceChat()) {
|
||||
svc.handleVoiceSignalPacketTypeRequest(new UUID(packet.uuidMost, packet.uuidLeast), conn);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClient(CPacketGetOtherClientUUIDV4EAG packet) {
|
||||
ProxiedPlayer player = plugin.getProxy().getPlayer(new UUID(packet.playerUUIDMost, packet.playerUUIDLeast));
|
||||
if(player != null) {
|
||||
PendingConnection conn2 = player.getPendingConnection();
|
||||
if(conn2 instanceof EaglerInitialHandler) {
|
||||
UUID uuid = ((EaglerInitialHandler)conn2).getClientBrandUUID();
|
||||
if (uuid != null) {
|
||||
((EaglerInitialHandler) conn.getPendingConnection())
|
||||
.sendEaglerMessage(new SPacketOtherPlayerClientUUIDV4EAG(packet.requestId,
|
||||
uuid.getMostSignificantBits(), uuid.getLeastSignificantBits()));
|
||||
return;
|
||||
}
|
||||
}else {
|
||||
((EaglerInitialHandler) conn.getPendingConnection())
|
||||
.sendEaglerMessage(new SPacketOtherPlayerClientUUIDV4EAG(packet.requestId,
|
||||
EaglerXBungeeAPIHelper.BRAND_VANILLA_UUID.getMostSignificantBits(),
|
||||
EaglerXBungeeAPIHelper.BRAND_VANILLA_UUID.getLeastSignificantBits()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
((EaglerInitialHandler) conn.getPendingConnection()).sendEaglerMessage(new SPacketOtherPlayerClientUUIDV4EAG(packet.requestId, 0l, 0l));
|
||||
}
|
||||
|
||||
public void handleClient(CPacketRequestServerInfoV4EAG packet) {
|
||||
EaglerPauseMenuConfig conf = plugin.getConfig().getPauseMenuConf();
|
||||
if (conf != null && conf.getEnabled()
|
||||
&& conf.getPacket().serverInfoMode == SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_SHOW_EMBED_OVER_WS
|
||||
&& Arrays.equals(conf.getServerInfoHash(), packet.requestHash)) {
|
||||
EaglerInitialHandler eaglerHandler = (EaglerInitialHandler)conn.getPendingConnection();
|
||||
synchronized(eaglerHandler.serverInfoSendBuffer) {
|
||||
if(eaglerHandler.hasSentServerInfo.getAndSet(true)) {
|
||||
conn.disconnect(new TextComponent("Duplicate server info request"));
|
||||
return;
|
||||
}
|
||||
eaglerHandler.serverInfoSendBuffer.clear();
|
||||
eaglerHandler.serverInfoSendBuffer.addAll(conf.getServerInfo());
|
||||
}
|
||||
}else {
|
||||
conn.disconnect(new TextComponent("Invalid server info request"));
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClient(CPacketWebViewMessageV4EAG packet) {
|
||||
EaglerInitialHandler eaglerHandler = (EaglerInitialHandler)conn.getPendingConnection();
|
||||
if(eaglerHandler.isWebViewChannelAllowed) {
|
||||
if(eaglerHandler.webViewMessageChannelOpen.get()) {
|
||||
if(eaglerHandler.getRPCEventSubscribed(EnumSubscribedEvent.WEBVIEW_MESSAGE)) {
|
||||
eaglerHandler.getRPCSessionHandler().sendRPCPacket(new SPacketRPCEventWebViewMessage(
|
||||
eaglerHandler.webViewMessageChannelName, packet.type, packet.data));
|
||||
}
|
||||
BungeeCord.getInstance().getPluginManager().callEvent(new EaglercraftWebViewMessageEvent(conn,
|
||||
eaglerHandler.getEaglerListenerConfig(), eaglerHandler.webViewMessageChannelName, packet));
|
||||
}
|
||||
}else {
|
||||
conn.disconnect(new TextComponent("Webview channel permissions have not been enabled!"));
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClient(CPacketWebViewMessageEnV4EAG packet) {
|
||||
EaglerInitialHandler eaglerHandler = (EaglerInitialHandler)conn.getPendingConnection();
|
||||
if(eaglerHandler.isWebViewChannelAllowed) {
|
||||
eaglerHandler.webViewMessageChannelOpen.set(packet.messageChannelOpen);
|
||||
String oldChannelName = eaglerHandler.webViewMessageChannelName;
|
||||
eaglerHandler.webViewMessageChannelName = packet.messageChannelOpen ? packet.channelName : null;
|
||||
if(eaglerHandler.getRPCEventSubscribed(EnumSubscribedEvent.WEBVIEW_OPEN_CLOSE)) {
|
||||
eaglerHandler.getRPCSessionHandler().sendRPCPacket(new SPacketRPCEventWebViewOpenClose(
|
||||
packet.messageChannelOpen, packet.messageChannelOpen ? packet.channelName : oldChannelName));
|
||||
}
|
||||
BungeeCord.getInstance().getPluginManager()
|
||||
.callEvent(new EaglercraftWebViewChannelEvent(conn, eaglerHandler.getEaglerListenerConfig(),
|
||||
packet.messageChannelOpen ? eaglerHandler.webViewMessageChannelName : oldChannelName,
|
||||
packet.messageChannelOpen ? EaglercraftWebViewChannelEvent.EventType.CHANNEL_OPEN
|
||||
: EaglercraftWebViewChannelEvent.EventType.CHANNEL_CLOSE));
|
||||
}else {
|
||||
conn.disconnect(new TextComponent("Webview channel permissions have not been enabled!"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -7,6 +7,7 @@ import java.util.List;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.query.EaglerQuerySimpleHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.query.MOTDConnection;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
|
||||
@ -47,7 +48,7 @@ public class MOTDQueryHandler extends EaglerQuerySimpleHandler implements MOTDCo
|
||||
|
||||
@Override
|
||||
protected void begin(String queryType) {
|
||||
creationTime = System.currentTimeMillis();
|
||||
creationTime = EaglerXBungeeAPIHelper.steadyTimeMillis();
|
||||
subType = queryType;
|
||||
returnType = "MOTD";
|
||||
EaglerListenerConfig listener = getListener();
|
||||
@ -60,7 +61,7 @@ public class MOTDQueryHandler extends EaglerQuerySimpleHandler implements MOTDCo
|
||||
}
|
||||
maxPlayers = listener.getMaxPlayers();
|
||||
onlinePlayers = ProxyServer.getInstance().getOnlineCount();
|
||||
players = new ArrayList();
|
||||
players = new ArrayList<>();
|
||||
for(ProxiedPlayer pp : ProxyServer.getInstance().getPlayers()) {
|
||||
players.add(pp.getDisplayName());
|
||||
if(players.size() >= 9) {
|
||||
|
@ -28,12 +28,13 @@ import net.md_5.bungee.api.plugin.PluginDescription;
|
||||
*/
|
||||
public class QueryManager {
|
||||
|
||||
private static final Map<String, Class<? extends HttpServerQueryHandler>> queryTypes = new HashMap();
|
||||
private static final Map<String, Class<? extends HttpServerQueryHandler>> queryTypes = new HashMap<>();
|
||||
|
||||
static {
|
||||
queryTypes.put("motd", MOTDQueryHandler.class);
|
||||
queryTypes.put("motd.cache", MOTDQueryHandler.class);
|
||||
queryTypes.put("version", VersionQueryHandler.class);
|
||||
queryTypes.put("revoke_session_token", RevokeSessionQueryHandler.class);
|
||||
}
|
||||
|
||||
public static HttpServerQueryHandler createQueryHandler(String type) {
|
||||
|
@ -0,0 +1,74 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.query;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftRevokeSessionQueryEvent;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftRevokeSessionQueryEvent.EnumSessionRevokeStatus;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.query.EaglerQueryHandler;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class RevokeSessionQueryHandler extends EaglerQueryHandler {
|
||||
|
||||
@Override
|
||||
protected void begin(String queryType) {
|
||||
this.setKeepAlive(true);
|
||||
this.acceptBinary();
|
||||
this.setMaxAge(5000l);
|
||||
this.sendStringResponse("revoke_session_token", "ready");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processString(String str) {
|
||||
this.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processJson(JsonObject obj) {
|
||||
this.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processBytes(byte[] bytes) {
|
||||
if(bytes.length > 255) {
|
||||
JsonObject response = new JsonObject();
|
||||
response.addProperty("status", "error");
|
||||
response.addProperty("code", 3);
|
||||
response.addProperty("delete", false);
|
||||
sendJsonResponseAndClose("revoke_session_token", response);
|
||||
return;
|
||||
}
|
||||
this.setMaxAge(30000l);
|
||||
EaglercraftRevokeSessionQueryEvent evt = new EaglercraftRevokeSessionQueryEvent(this.getAddress(), this.getOrigin(), bytes, this);
|
||||
BungeeCord.getInstance().getPluginManager().callEvent(evt);
|
||||
JsonObject response = new JsonObject();
|
||||
EnumSessionRevokeStatus stat = evt.getResultStatus();
|
||||
response.addProperty("status", stat.status);
|
||||
if(stat.code != -1) {
|
||||
response.addProperty("code", stat.code);
|
||||
}
|
||||
if(stat != EnumSessionRevokeStatus.SUCCESS) {
|
||||
response.addProperty("delete", evt.getShouldDeleteCookie());
|
||||
}
|
||||
sendJsonResponseAndClose("revoke_session_token", response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void closed() {
|
||||
}
|
||||
|
||||
}
|
@ -27,8 +27,13 @@ public class VersionQueryHandler extends EaglerQuerySimpleHandler {
|
||||
protected void begin(String queryType) {
|
||||
JsonObject responseObj = new JsonObject();
|
||||
JsonArray handshakeVersions = new JsonArray();
|
||||
handshakeVersions.add(2);
|
||||
handshakeVersions.add(3);
|
||||
if(this.getListener().isAllowV3()) {
|
||||
handshakeVersions.add(2);
|
||||
handshakeVersions.add(3);
|
||||
}
|
||||
if(this.getListener().isAllowV4()) {
|
||||
handshakeVersions.add(4);
|
||||
}
|
||||
responseObj.add("handshakeVersions", handshakeVersions);
|
||||
JsonArray protocolVersions = new JsonArray();
|
||||
protocolVersions.add(47);
|
||||
|
@ -27,7 +27,7 @@ public class HttpContentType {
|
||||
public final String cacheControlHeader;
|
||||
public final long fileBrowserCacheTTL;
|
||||
|
||||
public static final HttpContentType defaultType = new HttpContentType(new HashSet(), "application/octet-stream", null, 14400000l);
|
||||
public static final HttpContentType defaultType = new HttpContentType(new HashSet<>(), "application/octet-stream", null, 14400000l);
|
||||
|
||||
public HttpContentType(Set<String> extensions, String mimeType, String charset, long fileBrowserCacheTTL) {
|
||||
this.extensions = extensions;
|
||||
|
@ -4,16 +4,17 @@ import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2023 lax1dude. All Rights Reserved.
|
||||
@ -44,13 +45,13 @@ public class HttpWebServer {
|
||||
public HttpWebServer(File directory, Map<String,HttpContentType> contentTypes, List<String> index, String page404) {
|
||||
this.directory = directory;
|
||||
this.contentTypes = contentTypes;
|
||||
this.filesCache = new HashMap();
|
||||
this.filesCache = new HashMap<>();
|
||||
this.index = index;
|
||||
this.page404 = page404;
|
||||
}
|
||||
|
||||
public void flushCache() {
|
||||
long millis = System.currentTimeMillis();
|
||||
long millis = EaglerXBungeeAPIHelper.steadyTimeMillis();
|
||||
synchronized(cacheClearLock) {
|
||||
synchronized(filesCache) {
|
||||
Iterator<HttpMemoryCache> itr = filesCache.values().iterator();
|
||||
@ -69,7 +70,7 @@ public class HttpWebServer {
|
||||
try {
|
||||
String[] pathSplit = path.split("(\\\\|\\/)+");
|
||||
|
||||
List<String> pathList = pathSplit.length == 0 ? null : new ArrayList();
|
||||
List<String> pathList = pathSplit.length == 0 ? null : new ArrayList<>(pathSplit.length);
|
||||
for(int i = 0; i < pathSplit.length; ++i) {
|
||||
pathSplit[i] = pathSplit[i].trim();
|
||||
if(pathSplit[i].length() > 0) {
|
||||
@ -197,7 +198,7 @@ public class HttpWebServer {
|
||||
if(ct == null) {
|
||||
ct = HttpContentType.defaultType;
|
||||
}
|
||||
long millis = System.currentTimeMillis();
|
||||
long millis = EaglerXBungeeAPIHelper.steadyTimeMillis();
|
||||
return new HttpMemoryCache(path, requestCachePath, file, ct, millis, millis, path.lastModified());
|
||||
}catch(Throwable t) {
|
||||
return null;
|
||||
@ -208,7 +209,7 @@ public class HttpWebServer {
|
||||
if(file.fileObject == null) {
|
||||
return file;
|
||||
}
|
||||
long millis = System.currentTimeMillis();
|
||||
long millis = EaglerXBungeeAPIHelper.steadyTimeMillis();
|
||||
file.lastCacheHit = millis;
|
||||
if(millis - file.lastDiskReload > 4000l) {
|
||||
File f = file.fileObject;
|
||||
@ -264,8 +265,8 @@ public class HttpWebServer {
|
||||
+ "The requested resource <span id=\"addr\" style=\"font-family:monospace;font-weight:bold;background-color:#EEEEEE;padding:3px 4px;\">"
|
||||
+ "</span> could not be found on this server!</p><p>" + htmlEntities(plugin.getDescription().getName()) + "/"
|
||||
+ htmlEntities(plugin.getDescription().getVersion()) + "</p></body></html>").getBytes(StandardCharsets.UTF_8);
|
||||
HttpContentType htmlContentType = new HttpContentType(new HashSet(Arrays.asList("html")), "text/html", "utf-8", 120000l);
|
||||
long millis = System.currentTimeMillis();
|
||||
HttpContentType htmlContentType = new HttpContentType(Sets.newHashSet("html"), "text/html", "utf-8", 120000l);
|
||||
long millis = EaglerXBungeeAPIHelper.steadyTimeMillis();
|
||||
return new HttpMemoryCache(null, "~404", Unpooled.wrappedBuffer(src), htmlContentType, millis, millis, millis);
|
||||
}
|
||||
|
||||
@ -280,8 +281,8 @@ public class HttpWebServer {
|
||||
+ "404 'Websocket Upgrade Failure' (rip)</h1><h3>The URL you have requested is the physical WebSocket address of '" + name + "'</h3><p style=\"font-size:1.2em;"
|
||||
+ "line-height:1.3em;\">To correctly join this server, load the latest EaglercraftX 1.8 client, click the 'Direct Connect' button<br />on the 'Multiplayer' screen, "
|
||||
+ "and enter <span id=\"wsUri\">this URL</span> as the server address</p></body></html>").getBytes(StandardCharsets.UTF_8);
|
||||
HttpContentType htmlContentType = new HttpContentType(new HashSet(Arrays.asList("html")), "text/html", "utf-8", 14400000l);
|
||||
long millis = System.currentTimeMillis();
|
||||
HttpContentType htmlContentType = new HttpContentType(Sets.newHashSet("html"), "text/html", "utf-8", 14400000l);
|
||||
long millis = EaglerXBungeeAPIHelper.steadyTimeMillis();
|
||||
return new HttpMemoryCache(null, "~404", Unpooled.wrappedBuffer(src), htmlContentType, millis, millis, millis);
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,7 @@ public class AsyncSkinProvider {
|
||||
byte[] loadedPixels = new byte[16384];
|
||||
image.getRGB(0, 0, 64, 64, tmp, 0, 64);
|
||||
SkinRescaler.convertToBytes(tmp, loadedPixels);
|
||||
SkinPackets.setAlphaForChest(loadedPixels, (byte)255, 0);
|
||||
SkinPackets.setAlphaForChestV3(loadedPixels);
|
||||
doAccept(loadedPixels);
|
||||
return;
|
||||
}else if(srcWidth == 64 && srcHeight == 32) {
|
||||
@ -116,7 +116,7 @@ public class AsyncSkinProvider {
|
||||
byte[] loadedPixels = new byte[16384];
|
||||
image.getRGB(0, 0, 64, 32, tmp1, 0, 64);
|
||||
SkinRescaler.convert64x32To64x64(tmp1, loadedPixels);
|
||||
SkinPackets.setAlphaForChest(loadedPixels, (byte)255, 0);
|
||||
SkinPackets.setAlphaForChestV3(loadedPixels);
|
||||
doAccept(loadedPixels);
|
||||
return;
|
||||
}else {
|
||||
|
@ -9,6 +9,8 @@ import java.util.function.Consumer;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
@ -180,7 +182,7 @@ public class BinaryHttpClient {
|
||||
buffer.readBytes(array);
|
||||
buffer.release();
|
||||
}else {
|
||||
array = new byte[0];
|
||||
array = ArrayUtils.EMPTY_BYTE_ARRAY;
|
||||
}
|
||||
responseCallback.accept(new Response(responseCode, array));
|
||||
}finally {
|
||||
|
@ -3,7 +3,9 @@ package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins;
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapeCustomEAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapePresetEAG;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
@ -24,56 +26,29 @@ public class CapePackets {
|
||||
|
||||
public static final int PACKET_MY_CAPE_PRESET = 0x01;
|
||||
public static final int PACKET_MY_CAPE_CUSTOM = 0x02;
|
||||
public static final int PACKET_GET_OTHER_CAPE = 0x03;
|
||||
public static final int PACKET_OTHER_CAPE_PRESET = 0x04;
|
||||
public static final int PACKET_OTHER_CAPE_CUSTOM = 0x05;
|
||||
|
||||
public static void processPacket(byte[] data, UserConnection sender, CapeServiceOffline capeService) throws IOException {
|
||||
if(data.length == 0) {
|
||||
throw new IOException("Zero-length packet recieved");
|
||||
}
|
||||
int packetId = (int)data[0] & 0xFF;
|
||||
try {
|
||||
switch(packetId) {
|
||||
case PACKET_GET_OTHER_CAPE:
|
||||
processGetOtherCape(data, sender, capeService);
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Unknown packet type " + packetId);
|
||||
}
|
||||
}catch(IOException ex) {
|
||||
throw ex;
|
||||
}catch(Throwable t) {
|
||||
throw new IOException("Unhandled exception handling cape packet type " + packetId, t);
|
||||
}
|
||||
}
|
||||
|
||||
private static void processGetOtherCape(byte[] data, UserConnection sender, CapeServiceOffline capeService) throws IOException {
|
||||
if(data.length != 17) {
|
||||
throw new IOException("Invalid length " + data.length + " for skin request packet");
|
||||
}
|
||||
UUID searchUUID = SkinPackets.bytesToUUID(data, 1);
|
||||
capeService.processGetOtherCape(searchUUID, sender);
|
||||
}
|
||||
|
||||
public static void registerEaglerPlayer(UUID clientUUID, byte[] bs, CapeServiceOffline capeService) throws IOException {
|
||||
if(bs.length == 0) {
|
||||
throw new IOException("Zero-length packet recieved");
|
||||
}
|
||||
byte[] generatedPacket;
|
||||
GameMessagePacket generatedPacket;
|
||||
int packetType = (int)bs[0] & 0xFF;
|
||||
switch(packetType) {
|
||||
case PACKET_MY_CAPE_PRESET:
|
||||
if(bs.length != 5) {
|
||||
throw new IOException("Invalid length " + bs.length + " for preset cape packet");
|
||||
}
|
||||
generatedPacket = CapePackets.makePresetResponse(clientUUID, (bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF));
|
||||
generatedPacket = new SPacketOtherCapePresetEAG(clientUUID.getMostSignificantBits(),
|
||||
clientUUID.getLeastSignificantBits(), (bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF));
|
||||
break;
|
||||
case PACKET_MY_CAPE_CUSTOM:
|
||||
if(bs.length != 1174) {
|
||||
throw new IOException("Invalid length " + bs.length + " for custom cape packet");
|
||||
}
|
||||
generatedPacket = CapePackets.makeCustomResponse(clientUUID, bs, 1, 1173);
|
||||
byte[] capePixels = new byte[bs.length - 1];
|
||||
System.arraycopy(bs, 1, capePixels, 0, capePixels.length);
|
||||
generatedPacket = new SPacketOtherCapeCustomEAG(clientUUID.getMostSignificantBits(),
|
||||
clientUUID.getLeastSignificantBits(), capePixels);
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Unknown skin packet type: " + packetType);
|
||||
@ -82,29 +57,8 @@ public class CapePackets {
|
||||
}
|
||||
|
||||
public static void registerEaglerPlayerFallback(UUID clientUUID, CapeServiceOffline capeService) {
|
||||
capeService.registerEaglercraftPlayer(clientUUID, CapePackets.makePresetResponse(clientUUID, 0));
|
||||
capeService.registerEaglercraftPlayer(clientUUID, new SPacketOtherCapePresetEAG(
|
||||
clientUUID.getMostSignificantBits(), clientUUID.getLeastSignificantBits(), 0));
|
||||
}
|
||||
|
||||
public static byte[] makePresetResponse(UUID uuid, int presetId) {
|
||||
byte[] ret = new byte[1 + 16 + 4];
|
||||
ret[0] = (byte)PACKET_OTHER_CAPE_PRESET;
|
||||
SkinPackets.UUIDToBytes(uuid, ret, 1);
|
||||
ret[17] = (byte)(presetId >>> 24);
|
||||
ret[18] = (byte)(presetId >>> 16);
|
||||
ret[19] = (byte)(presetId >>> 8);
|
||||
ret[20] = (byte)(presetId & 0xFF);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static byte[] makeCustomResponse(UUID uuid, byte[] pixels) {
|
||||
return makeCustomResponse(uuid, pixels, 0, pixels.length);
|
||||
}
|
||||
|
||||
public static byte[] makeCustomResponse(UUID uuid, byte[] pixels, int offset, int length) {
|
||||
byte[] ret = new byte[1 + 16 + length];
|
||||
ret[0] = (byte)PACKET_OTHER_CAPE_CUSTOM;
|
||||
SkinPackets.UUIDToBytes(uuid, ret, 1);
|
||||
System.arraycopy(pixels, offset, ret, 17, length);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,11 @@ import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketForceClientCapeCustomV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketForceClientCapePresetV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapeCustomEAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapePresetEAG;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
|
||||
/**
|
||||
@ -26,26 +31,42 @@ public class CapeServiceOffline {
|
||||
|
||||
public static final int masterRateLimitPerPlayer = 250;
|
||||
|
||||
public static final String CHANNEL = "EAG|Capes-1.8";
|
||||
private final Map<UUID, GameMessagePacket> capesCache = new HashMap<>();
|
||||
|
||||
private final Map<UUID, byte[]> capesCache = new HashMap();
|
||||
|
||||
public void registerEaglercraftPlayer(UUID playerUUID, byte[] capePacket) {
|
||||
public void registerEaglercraftPlayer(UUID playerUUID, GameMessagePacket capePacket) {
|
||||
synchronized(capesCache) {
|
||||
capesCache.put(playerUUID, capePacket);
|
||||
}
|
||||
}
|
||||
|
||||
public void processGetOtherCape(UUID searchUUID, UserConnection sender) {
|
||||
if(((EaglerInitialHandler)sender.getPendingConnection()).skinLookupRateLimiter.rateLimit(masterRateLimitPerPlayer)) {
|
||||
byte[] maybeCape;
|
||||
EaglerInitialHandler initialHandler = (EaglerInitialHandler)sender.getPendingConnection();
|
||||
if(initialHandler.skinLookupRateLimiter.rateLimit(masterRateLimitPerPlayer)) {
|
||||
GameMessagePacket maybeCape;
|
||||
synchronized(capesCache) {
|
||||
maybeCape = capesCache.get(searchUUID);
|
||||
}
|
||||
if(maybeCape != null) {
|
||||
sender.sendData(CapeServiceOffline.CHANNEL, maybeCape);
|
||||
initialHandler.sendEaglerMessage(maybeCape);
|
||||
}else {
|
||||
sender.sendData(CapeServiceOffline.CHANNEL, CapePackets.makePresetResponse(searchUUID, 0));
|
||||
initialHandler.sendEaglerMessage(new SPacketOtherCapePresetEAG(searchUUID.getMostSignificantBits(),
|
||||
searchUUID.getLeastSignificantBits(), 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void processForceCape(UUID clientUUID, EaglerInitialHandler initialHandler) {
|
||||
GameMessagePacket maybeCape;
|
||||
synchronized(capesCache) {
|
||||
maybeCape = capesCache.get(clientUUID);
|
||||
}
|
||||
if(maybeCape != null) {
|
||||
if (maybeCape instanceof SPacketOtherCapePresetEAG) {
|
||||
initialHandler.sendEaglerMessage(
|
||||
new SPacketForceClientCapePresetV4EAG(((SPacketOtherCapePresetEAG) maybeCape).presetCape));
|
||||
} else if (maybeCape instanceof SPacketOtherCapeCustomEAG) {
|
||||
initialHandler.sendEaglerMessage(
|
||||
new SPacketForceClientCapeCustomV4EAG(((SPacketOtherCapeCustomEAG) maybeCape).customCape));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -56,6 +77,37 @@ public class CapeServiceOffline {
|
||||
}
|
||||
}
|
||||
|
||||
public GameMessagePacket getCape(UUID clientUUID) {
|
||||
synchronized(capesCache) {
|
||||
return capesCache.get(clientUUID);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getCapeHandshakeData(UUID clientUUID) {
|
||||
GameMessagePacket capePacket = getCape(clientUUID);
|
||||
if(capePacket != null) {
|
||||
if(capePacket instanceof SPacketOtherCapeCustomEAG) {
|
||||
SPacketOtherCapeCustomEAG pkt = (SPacketOtherCapeCustomEAG)capePacket;
|
||||
byte[] ret = new byte[1174];
|
||||
ret[0] = (byte)2;
|
||||
System.arraycopy(pkt.customCape, 0, ret, 1, 1173);
|
||||
return ret;
|
||||
}else {
|
||||
SPacketOtherCapePresetEAG pkt = (SPacketOtherCapePresetEAG)capePacket;
|
||||
int p = pkt.presetCape;
|
||||
byte[] ret = new byte[5];
|
||||
ret[0] = (byte)1;
|
||||
ret[1] = (byte)(p >>> 24);
|
||||
ret[2] = (byte)(p >>> 16);
|
||||
ret[3] = (byte)(p >>> 8);
|
||||
ret[4] = (byte)(p & 0xFF);
|
||||
return ret;
|
||||
}
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
synchronized(capesCache) {
|
||||
capesCache.clear();
|
||||
|
@ -1,8 +1,9 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
|
||||
/**
|
||||
@ -29,7 +30,7 @@ public interface ISkinService {
|
||||
|
||||
void processGetOtherSkin(UUID searchUUID, String skinURL, UserConnection sender);
|
||||
|
||||
void registerEaglercraftPlayer(UUID clientUUID, byte[] generatedPacket, int modelId) throws IOException;
|
||||
void registerEaglercraftPlayer(UUID clientUUID, SkinPacketVersionCache generatedPacket, int modelId);
|
||||
|
||||
void unregisterPlayer(UUID clientUUID);
|
||||
|
||||
@ -43,4 +44,8 @@ public interface ISkinService {
|
||||
|
||||
void shutdown();
|
||||
|
||||
void processForceSkin(UUID playerUUID, EaglerInitialHandler initialHandler);
|
||||
|
||||
SkinPacketVersionCache getSkin(UUID playerUUID);
|
||||
|
||||
}
|
||||
|
@ -14,7 +14,11 @@ import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.sqlite.EaglerDrivers;
|
||||
|
||||
/**
|
||||
@ -160,7 +164,7 @@ public class JDBCCacheProvider implements ICacheProvider {
|
||||
throw new CacheException("SQL query failure while loading cached skin", ex);
|
||||
}
|
||||
if(queriedLength == 0) {
|
||||
return new CacheLoadedSkin(uuid, queriedUrls, new byte[0]);
|
||||
return new CacheLoadedSkin(uuid, queriedUrls, ArrayUtils.EMPTY_BYTE_ARRAY);
|
||||
}else {
|
||||
byte[] decompressed = new byte[queriedLength];
|
||||
try {
|
||||
@ -305,8 +309,9 @@ public class JDBCCacheProvider implements ICacheProvider {
|
||||
@Override
|
||||
public void flush() {
|
||||
long millis = System.currentTimeMillis();
|
||||
if(millis - lastFlush > 1200000l) { // 30 minutes
|
||||
lastFlush = millis;
|
||||
long steadyMillis = EaglerXBungeeAPIHelper.steadyTimeMillis();
|
||||
if(steadyMillis - lastFlush > 1200000l) { // 30 minutes
|
||||
lastFlush = steadyMillis;
|
||||
try {
|
||||
Date expiryObjects = new Date(millis - keepObjectsDays * 86400000l);
|
||||
Date expiryProfiles = new Date(millis - keepProfilesDays * 86400000l);
|
||||
|
@ -1,5 +1,7 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2023 lax1dude. All Rights Reserved.
|
||||
*
|
||||
@ -21,13 +23,13 @@ public class SimpleRateLimiter {
|
||||
private int count;
|
||||
|
||||
public SimpleRateLimiter() {
|
||||
timer = System.currentTimeMillis();
|
||||
timer = EaglerXBungeeAPIHelper.steadyTimeMillis();
|
||||
count = 0;
|
||||
}
|
||||
|
||||
public boolean rateLimit(int maxPerMinute) {
|
||||
int t = 60000 / maxPerMinute;
|
||||
long millis = System.currentTimeMillis();
|
||||
long millis = EaglerXBungeeAPIHelper.steadyTimeMillis();
|
||||
int decr = (int)(millis - timer) / t;
|
||||
if(decr > 0) {
|
||||
timer += decr * t;
|
||||
|
@ -1,12 +1,13 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.UUID;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinCustomV3EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinCustomV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinPresetEAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
|
||||
@ -27,79 +28,13 @@ public class SkinPackets {
|
||||
|
||||
public static final int PACKET_MY_SKIN_PRESET = 0x01;
|
||||
public static final int PACKET_MY_SKIN_CUSTOM = 0x02;
|
||||
public static final int PACKET_GET_OTHER_SKIN = 0x03;
|
||||
public static final int PACKET_OTHER_SKIN_PRESET = 0x04;
|
||||
public static final int PACKET_OTHER_SKIN_CUSTOM = 0x05;
|
||||
public static final int PACKET_GET_SKIN_BY_URL = 0x06;
|
||||
|
||||
public static void processPacket(byte[] data, UserConnection sender, ISkinService skinService) throws IOException {
|
||||
if(data.length == 0) {
|
||||
throw new IOException("Zero-length packet recieved");
|
||||
}
|
||||
int packetId = (int)data[0] & 0xFF;
|
||||
try {
|
||||
switch(packetId) {
|
||||
case PACKET_GET_OTHER_SKIN:
|
||||
processGetOtherSkin(data, sender, skinService);
|
||||
break;
|
||||
case PACKET_GET_SKIN_BY_URL:
|
||||
processGetOtherSkinByURL(data, sender, skinService);
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Unknown packet type " + packetId);
|
||||
}
|
||||
}catch(IOException ex) {
|
||||
throw ex;
|
||||
}catch(Throwable t) {
|
||||
throw new IOException("Unhandled exception handling packet type " + packetId, t);
|
||||
}
|
||||
}
|
||||
|
||||
private static void processGetOtherSkin(byte[] data, UserConnection sender, ISkinService skinService) throws IOException {
|
||||
if(data.length != 17) {
|
||||
throw new IOException("Invalid length " + data.length + " for skin request packet");
|
||||
}
|
||||
UUID searchUUID = bytesToUUID(data, 1);
|
||||
skinService.processGetOtherSkin(searchUUID, sender);
|
||||
}
|
||||
|
||||
private static void processGetOtherSkinByURL(byte[] data, UserConnection sender, ISkinService skinService) throws IOException {
|
||||
if(data.length < 20) {
|
||||
throw new IOException("Invalid length " + data.length + " for skin request packet");
|
||||
}
|
||||
UUID searchUUID = bytesToUUID(data, 1);
|
||||
int urlLength = (data[17] << 8) | data[18];
|
||||
if(data.length < 19 + urlLength) {
|
||||
throw new IOException("Invalid length " + data.length + " for skin request packet with " + urlLength + " length URL");
|
||||
}
|
||||
String urlStr = bytesToAscii(data, 19, urlLength);
|
||||
urlStr = SkinService.sanitizeTextureURL(urlStr);
|
||||
if(urlStr == null) {
|
||||
throw new IOException("Invalid URL for skin request packet");
|
||||
}
|
||||
URL url;
|
||||
try {
|
||||
url = new URL(urlStr);
|
||||
}catch(MalformedURLException t) {
|
||||
throw new IOException("Invalid URL for skin request packet", t);
|
||||
}
|
||||
String host = url.getHost();
|
||||
if(EaglerXBungee.getEagler().getConfig().isValidSkinHost(host)) {
|
||||
UUID validUUID = createEaglerURLSkinUUID(urlStr);
|
||||
if(!searchUUID.equals(validUUID)) {
|
||||
throw new IOException("Invalid generated UUID from skin URL");
|
||||
}
|
||||
skinService.processGetOtherSkin(searchUUID, urlStr, sender);
|
||||
}else {
|
||||
throw new IOException("Invalid host in skin packet: " + host);
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerEaglerPlayer(UUID clientUUID, byte[] bs, ISkinService skinService) throws IOException {
|
||||
public static void registerEaglerPlayer(UUID clientUUID, byte[] bs, ISkinService skinService, int protocolVers) throws IOException {
|
||||
if(bs.length == 0) {
|
||||
throw new IOException("Zero-length packet recieved");
|
||||
}
|
||||
byte[] generatedPacket;
|
||||
GameMessagePacket generatedPacketV3 = null;
|
||||
GameMessagePacket generatedPacketV4 = null;
|
||||
int skinModel = -1;
|
||||
int packetType = (int)bs[0] & 0xFF;
|
||||
switch(packetType) {
|
||||
@ -107,87 +42,60 @@ public class SkinPackets {
|
||||
if(bs.length != 5) {
|
||||
throw new IOException("Invalid length " + bs.length + " for preset skin packet");
|
||||
}
|
||||
generatedPacket = SkinPackets.makePresetResponse(clientUUID, (bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF));
|
||||
generatedPacketV3 = generatedPacketV4 = new SPacketOtherSkinPresetEAG(clientUUID.getMostSignificantBits(),
|
||||
clientUUID.getLeastSignificantBits(), (bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF));
|
||||
break;
|
||||
case PACKET_MY_SKIN_CUSTOM:
|
||||
if(bs.length != 2 + 16384) {
|
||||
throw new IOException("Invalid length " + bs.length + " for custom skin packet");
|
||||
if(protocolVers <= 3) {
|
||||
byte[] pixels = new byte[16384];
|
||||
if(bs.length != 2 + pixels.length) {
|
||||
throw new IOException("Invalid length " + bs.length + " for custom skin packet");
|
||||
}
|
||||
setAlphaForChestV3(pixels);
|
||||
System.arraycopy(bs, 2, pixels, 0, pixels.length);
|
||||
generatedPacketV3 = new SPacketOtherSkinCustomV3EAG(clientUUID.getMostSignificantBits(), clientUUID.getLeastSignificantBits(), (skinModel = (int)bs[1] & 0xFF), pixels);
|
||||
}else {
|
||||
byte[] pixels = new byte[12288];
|
||||
if(bs.length != 2 + pixels.length) {
|
||||
throw new IOException("Invalid length " + bs.length + " for custom skin packet");
|
||||
}
|
||||
setAlphaForChestV4(pixels);
|
||||
System.arraycopy(bs, 2, pixels, 0, pixels.length);
|
||||
generatedPacketV4 = new SPacketOtherSkinCustomV4EAG(clientUUID.getMostSignificantBits(), clientUUID.getLeastSignificantBits(), (skinModel = (int)bs[1] & 0xFF), pixels);
|
||||
}
|
||||
setAlphaForChest(bs, (byte)255, 2);
|
||||
generatedPacket = SkinPackets.makeCustomResponse(clientUUID, (skinModel = (int)bs[1] & 0xFF), bs, 2, 16384);
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Unknown skin packet type: " + packetType);
|
||||
}
|
||||
skinService.registerEaglercraftPlayer(clientUUID, generatedPacket, skinModel);
|
||||
skinService.registerEaglercraftPlayer(clientUUID, new SkinPacketVersionCache(generatedPacketV3, generatedPacketV4), skinModel);
|
||||
}
|
||||
|
||||
public static void registerEaglerPlayerFallback(UUID clientUUID, ISkinService skinService) throws IOException {
|
||||
int skinModel = (clientUUID.hashCode() & 1) != 0 ? 1 : 0;
|
||||
byte[] generatedPacket = SkinPackets.makePresetResponse(clientUUID, skinModel);
|
||||
skinService.registerEaglercraftPlayer(clientUUID, generatedPacket, skinModel);
|
||||
skinService.registerEaglercraftPlayer(clientUUID, SkinPacketVersionCache.createPreset(
|
||||
clientUUID.getMostSignificantBits(), clientUUID.getLeastSignificantBits(), skinModel), skinModel);
|
||||
}
|
||||
|
||||
public static void setAlphaForChest(byte[] skin64x64, byte alpha, int offset) {
|
||||
if(skin64x64.length - offset != 16384) {
|
||||
public static void setAlphaForChestV3(byte[] skin64x64) {
|
||||
if(skin64x64.length != 16384) {
|
||||
throw new IllegalArgumentException("Skin is not 64x64!");
|
||||
}
|
||||
for(int y = 20; y < 32; ++y) {
|
||||
for(int x = 16; x < 40; ++x) {
|
||||
skin64x64[offset + ((y << 8) | (x << 2))] = alpha;
|
||||
skin64x64[(y << 8) | (x << 2)] = (byte)0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] makePresetResponse(UUID uuid) {
|
||||
return makePresetResponse(uuid, (uuid.hashCode() & 1) != 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
public static byte[] makePresetResponse(UUID uuid, int presetId) {
|
||||
byte[] ret = new byte[1 + 16 + 4];
|
||||
ret[0] = (byte)PACKET_OTHER_SKIN_PRESET;
|
||||
UUIDToBytes(uuid, ret, 1);
|
||||
ret[17] = (byte)(presetId >>> 24);
|
||||
ret[18] = (byte)(presetId >>> 16);
|
||||
ret[19] = (byte)(presetId >>> 8);
|
||||
ret[20] = (byte)(presetId & 0xFF);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static byte[] makeCustomResponse(UUID uuid, int model, byte[] pixels) {
|
||||
return makeCustomResponse(uuid, model, pixels, 0, pixels.length);
|
||||
}
|
||||
|
||||
public static byte[] makeCustomResponse(UUID uuid, int model, byte[] pixels, int offset, int length) {
|
||||
byte[] ret = new byte[1 + 16 + 1 + length];
|
||||
ret[0] = (byte)PACKET_OTHER_SKIN_CUSTOM;
|
||||
UUIDToBytes(uuid, ret, 1);
|
||||
ret[17] = (byte)model;
|
||||
System.arraycopy(pixels, offset, ret, 18, length);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static UUID bytesToUUID(byte[] bytes, int off) {
|
||||
long msb = (((long) bytes[off] & 0xFFl) << 56l) | (((long) bytes[off + 1] & 0xFFl) << 48l)
|
||||
| (((long) bytes[off + 2] & 0xFFl) << 40l) | (((long) bytes[off + 3] & 0xFFl) << 32l)
|
||||
| (((long) bytes[off + 4] & 0xFFl) << 24l) | (((long) bytes[off + 5] & 0xFFl) << 16l)
|
||||
| (((long) bytes[off + 6] & 0xFFl) << 8l) | ((long) bytes[off + 7] & 0xFFl);
|
||||
long lsb = (((long) bytes[off + 8] & 0xFFl) << 56l) | (((long) bytes[off + 9] & 0xFFl) << 48l)
|
||||
| (((long) bytes[off + 10] & 0xFFl) << 40l) | (((long) bytes[off + 11] & 0xFFl) << 32l)
|
||||
| (((long) bytes[off + 12] & 0xFFl) << 24l) | (((long) bytes[off + 13] & 0xFFl) << 16l)
|
||||
| (((long) bytes[off + 14] & 0xFFl) << 8l) | ((long) bytes[off + 15] & 0xFFl);
|
||||
return new UUID(msb, lsb);
|
||||
}
|
||||
|
||||
private static final String hex = "0123456789abcdef";
|
||||
|
||||
public static String bytesToString(byte[] bytes, int off, int len) {
|
||||
char[] ret = new char[len << 1];
|
||||
for(int i = 0; i < len; ++i) {
|
||||
ret[i * 2] = hex.charAt((bytes[off + i] >> 4) & 0xF);
|
||||
ret[i * 2 + 1] = hex.charAt(bytes[off + i] & 0xF);
|
||||
public static void setAlphaForChestV4(byte[] skin64x64) {
|
||||
if(skin64x64.length != 12288) {
|
||||
throw new IllegalArgumentException("Skin is not 64x64!");
|
||||
}
|
||||
for(int y = 20; y < 32; ++y) {
|
||||
for(int x = 16; x < 40; ++x) {
|
||||
skin64x64[((y << 6) | x) * 3] |= 0x80;
|
||||
}
|
||||
}
|
||||
return new String(ret);
|
||||
}
|
||||
|
||||
public static String bytesToAscii(byte[] bytes, int off, int len) {
|
||||
@ -197,32 +105,11 @@ public class SkinPackets {
|
||||
}
|
||||
return new String(ret);
|
||||
}
|
||||
|
||||
|
||||
public static String bytesToAscii(byte[] bytes) {
|
||||
return bytesToAscii(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
public static void UUIDToBytes(UUID uuid, byte[] bytes, int off) {
|
||||
long msb = uuid.getMostSignificantBits();
|
||||
long lsb = uuid.getLeastSignificantBits();
|
||||
bytes[off] = (byte)(msb >>> 56l);
|
||||
bytes[off + 1] = (byte)(msb >>> 48l);
|
||||
bytes[off + 2] = (byte)(msb >>> 40l);
|
||||
bytes[off + 3] = (byte)(msb >>> 32l);
|
||||
bytes[off + 4] = (byte)(msb >>> 24l);
|
||||
bytes[off + 5] = (byte)(msb >>> 16l);
|
||||
bytes[off + 6] = (byte)(msb >>> 8l);
|
||||
bytes[off + 7] = (byte)(msb & 0xFFl);
|
||||
bytes[off + 8] = (byte)(lsb >>> 56l);
|
||||
bytes[off + 9] = (byte)(lsb >>> 48l);
|
||||
bytes[off + 10] = (byte)(lsb >>> 40l);
|
||||
bytes[off + 11] = (byte)(lsb >>> 32l);
|
||||
bytes[off + 12] = (byte)(lsb >>> 24l);
|
||||
bytes[off + 13] = (byte)(lsb >>> 16l);
|
||||
bytes[off + 14] = (byte)(lsb >>> 8l);
|
||||
bytes[off + 15] = (byte)(lsb & 0xFFl);
|
||||
}
|
||||
|
||||
public static byte[] asciiString(String string) {
|
||||
byte[] str = new byte[string.length()];
|
||||
for(int i = 0; i < str.length; ++i) {
|
||||
@ -239,21 +126,4 @@ public class SkinPackets {
|
||||
return "slim".equalsIgnoreCase(modelName) ? 1 : 0;
|
||||
}
|
||||
|
||||
public static byte[] rewriteUUID(UUID newUUID, byte[] pkt) {
|
||||
byte[] ret = new byte[pkt.length];
|
||||
System.arraycopy(pkt, 0, ret, 0, pkt.length);
|
||||
UUIDToBytes(newUUID, ret, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static byte[] rewriteUUIDModel(UUID newUUID, byte[] pkt, int model) {
|
||||
byte[] ret = new byte[pkt.length];
|
||||
System.arraycopy(pkt, 0, ret, 0, pkt.length);
|
||||
UUIDToBytes(newUUID, ret, 1);
|
||||
if(ret[0] == (byte)PACKET_OTHER_SKIN_CUSTOM) {
|
||||
ret[17] = (byte)model;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,5 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
@ -11,6 +10,8 @@ import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.MultimapBuilder;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinPresetEAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
|
||||
/**
|
||||
@ -35,16 +36,16 @@ public class SkinServiceOffline implements ISkinService {
|
||||
private static class CachedSkin {
|
||||
|
||||
protected final UUID uuid;
|
||||
protected final byte[] packet;
|
||||
protected final SkinPacketVersionCache packet;
|
||||
|
||||
protected CachedSkin(UUID uuid, byte[] packet) {
|
||||
protected CachedSkin(UUID uuid, SkinPacketVersionCache packet) {
|
||||
this.uuid = uuid;
|
||||
this.packet = packet;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final Map<UUID, CachedSkin> skinCache = new HashMap();
|
||||
private final Map<UUID, CachedSkin> skinCache = new HashMap<>();
|
||||
|
||||
private final Multimap<UUID, UUID> onlinePlayersFromTexturesMap = MultimapBuilder.hashKeys().hashSetValues().build();
|
||||
|
||||
@ -56,20 +57,23 @@ public class SkinServiceOffline implements ISkinService {
|
||||
}
|
||||
|
||||
public void processGetOtherSkin(UUID searchUUID, UserConnection sender) {
|
||||
if(((EaglerInitialHandler)sender.getPendingConnection()).skinLookupRateLimiter.rateLimit(masterRateLimitPerPlayer)) {
|
||||
EaglerInitialHandler initialHandler = (EaglerInitialHandler)sender.getPendingConnection();
|
||||
if(initialHandler.skinLookupRateLimiter.rateLimit(masterRateLimitPerPlayer)) {
|
||||
CachedSkin cached;
|
||||
synchronized(skinCache) {
|
||||
cached = skinCache.get(searchUUID);
|
||||
}
|
||||
if(cached != null) {
|
||||
sender.sendData(SkinService.CHANNEL, cached.packet);
|
||||
initialHandler.sendEaglerMessage(cached.packet.get(initialHandler.getEaglerProtocol()));
|
||||
}else {
|
||||
sender.sendData(SkinService.CHANNEL, SkinPackets.makePresetResponse(searchUUID));
|
||||
initialHandler.sendEaglerMessage(new SPacketOtherSkinPresetEAG(searchUUID.getMostSignificantBits(),
|
||||
searchUUID.getLeastSignificantBits(), (searchUUID.hashCode() & 1) != 0 ? 1 : 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void processGetOtherSkin(UUID searchUUID, String skinURL, UserConnection sender) {
|
||||
EaglerInitialHandler initialHandler = (EaglerInitialHandler)sender.getPendingConnection();
|
||||
Collection<UUID> uuids;
|
||||
synchronized(onlinePlayersFromTexturesMap) {
|
||||
uuids = onlinePlayersFromTexturesMap.get(searchUUID);
|
||||
@ -81,15 +85,23 @@ public class SkinServiceOffline implements ISkinService {
|
||||
while(uuidItr.hasNext()) {
|
||||
cached = skinCache.get(uuidItr.next());
|
||||
if(cached != null) {
|
||||
sender.sendData(SkinService.CHANNEL, SkinPackets.rewriteUUID(searchUUID, cached.packet));
|
||||
initialHandler.sendEaglerMessage(cached.packet.get(initialHandler.getEaglerProtocol(),
|
||||
searchUUID.getMostSignificantBits(), searchUUID.getLeastSignificantBits()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sender.sendData(SkinService.CHANNEL, SkinPackets.makePresetResponse(searchUUID));
|
||||
if(skinURL.startsWith("eagler://")) { // customs skulls from exported singleplayer worlds
|
||||
initialHandler.sendEaglerMessage(new SPacketOtherSkinPresetEAG(searchUUID.getMostSignificantBits(),
|
||||
searchUUID.getLeastSignificantBits(), 0));
|
||||
return;
|
||||
}
|
||||
initialHandler.sendEaglerMessage(new SPacketOtherSkinPresetEAG(searchUUID.getMostSignificantBits(),
|
||||
searchUUID.getLeastSignificantBits(), (searchUUID.hashCode() & 1) != 0 ? 1 : 0));
|
||||
}
|
||||
|
||||
public void registerEaglercraftPlayer(UUID clientUUID, byte[] generatedPacket, int modelId) throws IOException {
|
||||
public void registerEaglercraftPlayer(UUID clientUUID, SkinPacketVersionCache generatedPacket, int modelId) {
|
||||
synchronized(skinCache) {
|
||||
skinCache.put(clientUUID, new CachedSkin(clientUUID, generatedPacket));
|
||||
}
|
||||
@ -107,6 +119,16 @@ public class SkinServiceOffline implements ISkinService {
|
||||
}
|
||||
}
|
||||
|
||||
public void processForceSkin(UUID playerUUID, EaglerInitialHandler initialHandler) {
|
||||
CachedSkin cached;
|
||||
synchronized(skinCache) {
|
||||
cached = skinCache.get(playerUUID);
|
||||
}
|
||||
if(cached != null) {
|
||||
initialHandler.sendEaglerMessage(cached.packet.getForceClientV4());
|
||||
}
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
// no
|
||||
}
|
||||
@ -117,4 +139,12 @@ public class SkinServiceOffline implements ISkinService {
|
||||
}
|
||||
}
|
||||
|
||||
public SkinPacketVersionCache getSkin(UUID playerUUID) {
|
||||
CachedSkin cached;
|
||||
synchronized(skinCache) {
|
||||
cached = skinCache.get(playerUUID);
|
||||
}
|
||||
return cached != null ? cached.packet : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ public class EaglerDrivers {
|
||||
driversJARs.put(address, classLoader);
|
||||
}
|
||||
|
||||
Class loadedDriver;
|
||||
Class<?> loadedDriver;
|
||||
try {
|
||||
loadedDriver = classLoader.loadClass(driverClass);
|
||||
}catch(ClassNotFoundException ex) {
|
||||
@ -92,8 +92,8 @@ public class EaglerDrivers {
|
||||
return sqlDriver;
|
||||
}
|
||||
|
||||
private static final Map<String, URLClassLoader> driversJARs = new HashMap();
|
||||
private static final Map<String, Driver> driversDrivers = new HashMap();
|
||||
private static final Map<String, URLClassLoader> driversJARs = new HashMap<>();
|
||||
private static final Map<String, Driver> driversDrivers = new HashMap<>();
|
||||
|
||||
public static Connection connectToDatabase(String address, String driverClass, String driverPath, Properties props)
|
||||
throws SQLException {
|
||||
|
@ -5,6 +5,8 @@ import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022 ayunami2000. All Rights Reserved.
|
||||
*
|
||||
@ -42,7 +44,7 @@ public class ExpiringSet<T> extends HashSet<T> {
|
||||
|
||||
public void checkForExpirations() {
|
||||
Iterator<T> iterator = this.timestamps.keySet().iterator();
|
||||
long now = System.currentTimeMillis();
|
||||
long now = EaglerXBungeeAPIHelper.steadyTimeMillis();
|
||||
while (iterator.hasNext()) {
|
||||
T element = iterator.next();
|
||||
if (super.contains(element)) {
|
||||
@ -61,7 +63,7 @@ public class ExpiringSet<T> extends HashSet<T> {
|
||||
public boolean add(T o) {
|
||||
checkForExpirations();
|
||||
boolean success = super.add(o);
|
||||
if (success) timestamps.put(o, System.currentTimeMillis());
|
||||
if (success) timestamps.put(o, EaglerXBungeeAPIHelper.steadyTimeMillis());
|
||||
return success;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.voice;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@ -7,7 +9,13 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.server.SPacketRPCEventToggledVoice;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EnumVoiceState;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftVoiceStatusChangeEvent;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.backend_rpc_protocol.EnumSubscribedEvent;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
|
||||
@ -29,7 +37,7 @@ import net.md_5.bungee.api.config.ServerInfo;
|
||||
public class VoiceServerImpl {
|
||||
|
||||
private final ServerInfo server;
|
||||
private final byte[] iceServersPacket;
|
||||
private final GameMessagePacket iceServersPacket;
|
||||
|
||||
private final Map<UUID, UserConnection> voicePlayers = new HashMap<>();
|
||||
private final Map<UUID, ExpiringSet<UUID>> voiceRequests = new HashMap<>();
|
||||
@ -70,17 +78,23 @@ public class VoiceServerImpl {
|
||||
}
|
||||
}
|
||||
|
||||
VoiceServerImpl(ServerInfo server, byte[] iceServersPacket) {
|
||||
VoiceServerImpl(ServerInfo server, GameMessagePacket iceServersPacket) {
|
||||
this.server = server;
|
||||
this.iceServersPacket = iceServersPacket;
|
||||
}
|
||||
|
||||
public void handlePlayerLoggedIn(UserConnection player) {
|
||||
player.sendData(VoiceService.CHANNEL, iceServersPacket);
|
||||
EaglerInitialHandler eaglerHandler = (EaglerInitialHandler)player.getPendingConnection();
|
||||
eaglerHandler.sendEaglerMessage(iceServersPacket);
|
||||
eaglerHandler.fireVoiceStateChange(EaglercraftVoiceStatusChangeEvent.EnumVoiceState.DISABLED);
|
||||
if(eaglerHandler.getRPCEventSubscribed(EnumSubscribedEvent.TOGGLE_VOICE)) {
|
||||
eaglerHandler.getRPCSessionHandler().handleVoiceStateTransition(SPacketRPCEventToggledVoice.VOICE_STATE_DISABLED);
|
||||
}
|
||||
}
|
||||
|
||||
public void handlePlayerLoggedOut(UserConnection player) {
|
||||
removeUser(player.getUniqueId());
|
||||
EaglerInitialHandler eaglerHandler = (EaglerInitialHandler)player.getPendingConnection();
|
||||
removeUser(eaglerHandler.getUniqueId());
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeRequest(UUID player, UserConnection sender) {
|
||||
@ -115,106 +129,145 @@ public class VoiceServerImpl {
|
||||
voiceRequests.remove(senderUUID);
|
||||
// send each other add data
|
||||
voicePairs.add(newPair);
|
||||
targetPlayerCon.sendData(VoiceService.CHANNEL,
|
||||
VoiceSignalPackets.makeVoiceSignalPacketConnect(senderUUID, false));
|
||||
sender.sendData(VoiceService.CHANNEL, VoiceSignalPackets.makeVoiceSignalPacketConnect(player, true));
|
||||
EaglerInitialHandler targetInitialHandler = (EaglerInitialHandler) targetPlayerCon
|
||||
.getPendingConnection();
|
||||
if (targetInitialHandler.getEaglerProtocol().ver <= 3) {
|
||||
targetInitialHandler.sendEaglerMessage(new SPacketVoiceSignalConnectV3EAG(
|
||||
senderUUID.getMostSignificantBits(), senderUUID.getLeastSignificantBits(), false, false));
|
||||
} else {
|
||||
targetInitialHandler.sendEaglerMessage(new SPacketVoiceSignalConnectV4EAG(
|
||||
senderUUID.getMostSignificantBits(), senderUUID.getLeastSignificantBits(), false));
|
||||
}
|
||||
EaglerInitialHandler senderInitialHandler = (EaglerInitialHandler) sender.getPendingConnection();
|
||||
if (senderInitialHandler.getEaglerProtocol().ver <= 3) {
|
||||
senderInitialHandler.sendEaglerMessage(new SPacketVoiceSignalConnectV3EAG(
|
||||
player.getMostSignificantBits(), player.getLeastSignificantBits(), false, true));
|
||||
} else {
|
||||
senderInitialHandler.sendEaglerMessage(new SPacketVoiceSignalConnectV4EAG(
|
||||
player.getMostSignificantBits(), player.getLeastSignificantBits(), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeConnect(UserConnection sender) {
|
||||
if(!((EaglerInitialHandler)sender.getPendingConnection()).voiceConnectRateLimiter.rateLimit(VOICE_CONNECT_RATELIMIT)) {
|
||||
EaglerInitialHandler eaglerHandler = (EaglerInitialHandler)sender.getPendingConnection();
|
||||
if(!eaglerHandler.voiceConnectRateLimiter.rateLimit(VOICE_CONNECT_RATELIMIT)) {
|
||||
return;
|
||||
}
|
||||
eaglerHandler.fireVoiceStateChange(EaglercraftVoiceStatusChangeEvent.EnumVoiceState.ENABLED);
|
||||
if(eaglerHandler.getRPCEventSubscribed(EnumSubscribedEvent.TOGGLE_VOICE)) {
|
||||
eaglerHandler.getRPCSessionHandler().handleVoiceStateTransition(SPacketRPCEventToggledVoice.VOICE_STATE_ENABLED);
|
||||
}
|
||||
synchronized (voicePlayers) {
|
||||
if (voicePlayers.containsKey(sender.getUniqueId())) {
|
||||
if (voicePlayers.containsKey(eaglerHandler.getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
boolean hasNoOtherPlayers = voicePlayers.isEmpty();
|
||||
voicePlayers.put(sender.getUniqueId(), sender);
|
||||
voicePlayers.put(eaglerHandler.getUniqueId(), sender);
|
||||
if (hasNoOtherPlayers) {
|
||||
return;
|
||||
}
|
||||
byte[] packetToBroadcast = VoiceSignalPackets.makeVoiceSignalPacketGlobal(voicePlayers.values());
|
||||
Collection<SPacketVoiceSignalGlobalEAG.UserData> userDatas = new ArrayList<>(voicePlayers.size());
|
||||
for(UserConnection userCon : voicePlayers.values()) {
|
||||
UUID uuid = userCon.getUniqueId();
|
||||
userDatas.add(new SPacketVoiceSignalGlobalEAG.UserData(uuid.getMostSignificantBits(),
|
||||
uuid.getLeastSignificantBits(), userCon.getDisplayName()));
|
||||
}
|
||||
GameMessagePacket packetToBroadcast = new SPacketVoiceSignalGlobalEAG(userDatas);
|
||||
for (UserConnection userCon : voicePlayers.values()) {
|
||||
userCon.sendData(VoiceService.CHANNEL, packetToBroadcast);
|
||||
((EaglerInitialHandler)userCon.getPendingConnection()).sendEaglerMessage(packetToBroadcast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeICE(UUID player, String str, UserConnection sender) {
|
||||
void handleVoiceSignalPacketTypeICE(UUID player, byte[] str, UserConnection sender) {
|
||||
UserConnection pass;
|
||||
VoicePair pair = new VoicePair(player, sender.getUniqueId());
|
||||
synchronized (voicePlayers) {
|
||||
pass = voicePairs.contains(pair) ? voicePlayers.get(player) : null;
|
||||
}
|
||||
if (pass != null) {
|
||||
pass.sendData(VoiceService.CHANNEL, VoiceSignalPackets.makeVoiceSignalPacketICE(sender.getUniqueId(), str));
|
||||
UUID uuid = sender.getUniqueId();
|
||||
((EaglerInitialHandler) pass.getPendingConnection()).sendEaglerMessage(
|
||||
new SPacketVoiceSignalICEEAG(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits(), str));
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeDesc(UUID player, String str, UserConnection sender) {
|
||||
void handleVoiceSignalPacketTypeDesc(UUID player, byte[] str, UserConnection sender) {
|
||||
UserConnection pass;
|
||||
VoicePair pair = new VoicePair(player, sender.getUniqueId());
|
||||
synchronized (voicePlayers) {
|
||||
pass = voicePairs.contains(pair) ? voicePlayers.get(player) : null;
|
||||
}
|
||||
if (pass != null) {
|
||||
pass.sendData(VoiceService.CHANNEL,
|
||||
VoiceSignalPackets.makeVoiceSignalPacketDesc(sender.getUniqueId(), str));
|
||||
UUID uuid = sender.getUniqueId();
|
||||
((EaglerInitialHandler) pass.getPendingConnection()).sendEaglerMessage(
|
||||
new SPacketVoiceSignalDescEAG(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits(), str));
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeDisconnect(UUID player, UserConnection sender) {
|
||||
if (player != null) {
|
||||
synchronized (voicePlayers) {
|
||||
if (!voicePlayers.containsKey(player)) {
|
||||
return;
|
||||
}
|
||||
byte[] userDisconnectPacket = null;
|
||||
Iterator<VoicePair> pairsItr = voicePairs.iterator();
|
||||
while (pairsItr.hasNext()) {
|
||||
VoicePair voicePair = pairsItr.next();
|
||||
UUID target = null;
|
||||
if (voicePair.uuid1.equals(player)) {
|
||||
target = voicePair.uuid2;
|
||||
} else if (voicePair.uuid2.equals(player)) {
|
||||
target = voicePair.uuid1;
|
||||
}
|
||||
if (target != null) {
|
||||
pairsItr.remove();
|
||||
UserConnection conn = voicePlayers.get(target);
|
||||
if (conn != null) {
|
||||
if (userDisconnectPacket == null) {
|
||||
userDisconnectPacket = VoiceSignalPackets.makeVoiceSignalPacketDisconnect(player);
|
||||
}
|
||||
conn.sendData(VoiceService.CHANNEL, userDisconnectPacket);
|
||||
}
|
||||
sender.sendData(VoiceService.CHANNEL,
|
||||
VoiceSignalPackets.makeVoiceSignalPacketDisconnect(target));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
removeUser(sender.getUniqueId());
|
||||
}
|
||||
void handleVoiceSignalPacketTypeDisconnect(UserConnection sender) {
|
||||
removeUser(sender.getUniqueId());
|
||||
}
|
||||
|
||||
public void removeUser(UUID user) {
|
||||
void handleVoiceSignalPacketTypeDisconnectPeer(UUID player, UserConnection sender) {
|
||||
synchronized (voicePlayers) {
|
||||
if (voicePlayers.remove(user) == null) {
|
||||
if (!voicePlayers.containsKey(player)) {
|
||||
return;
|
||||
}
|
||||
Iterator<VoicePair> pairsItr = voicePairs.iterator();
|
||||
while (pairsItr.hasNext()) {
|
||||
VoicePair voicePair = pairsItr.next();
|
||||
UUID target = null;
|
||||
if (voicePair.uuid1.equals(player)) {
|
||||
target = voicePair.uuid2;
|
||||
} else if (voicePair.uuid2.equals(player)) {
|
||||
target = voicePair.uuid1;
|
||||
}
|
||||
if (target != null) {
|
||||
pairsItr.remove();
|
||||
UserConnection conn = voicePlayers.get(target);
|
||||
if (conn != null) {
|
||||
((EaglerInitialHandler) conn.getPendingConnection()).sendEaglerMessage(
|
||||
new SPacketVoiceSignalDisconnectPeerEAG(player.getMostSignificantBits(),
|
||||
player.getLeastSignificantBits()));
|
||||
}
|
||||
((EaglerInitialHandler) sender.getPendingConnection())
|
||||
.sendEaglerMessage(new SPacketVoiceSignalDisconnectPeerEAG(target.getMostSignificantBits(),
|
||||
target.getLeastSignificantBits()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void removeUser(UUID user) {
|
||||
synchronized (voicePlayers) {
|
||||
UserConnection connRemove;
|
||||
if ((connRemove = voicePlayers.remove(user)) == null) {
|
||||
return;
|
||||
}else {
|
||||
EaglerInitialHandler eaglerHandler = (EaglerInitialHandler)connRemove.getPendingConnection();
|
||||
eaglerHandler.fireVoiceStateChange(EaglercraftVoiceStatusChangeEvent.EnumVoiceState.DISABLED);
|
||||
if(eaglerHandler.getRPCEventSubscribed(EnumSubscribedEvent.TOGGLE_VOICE)) {
|
||||
eaglerHandler.getRPCSessionHandler().handleVoiceStateTransition(SPacketRPCEventToggledVoice.VOICE_STATE_DISABLED);
|
||||
}
|
||||
}
|
||||
voiceRequests.remove(user);
|
||||
if (voicePlayers.size() > 0) {
|
||||
byte[] voicePlayersPkt = VoiceSignalPackets.makeVoiceSignalPacketGlobal(voicePlayers.values());
|
||||
Collection<SPacketVoiceSignalGlobalEAG.UserData> userDatas = new ArrayList<>(voicePlayers.size());
|
||||
for(UserConnection userCon : voicePlayers.values()) {
|
||||
UUID uuid = userCon.getUniqueId();
|
||||
userDatas.add(new SPacketVoiceSignalGlobalEAG.UserData(uuid.getMostSignificantBits(),
|
||||
uuid.getLeastSignificantBits(), userCon.getDisplayName()));
|
||||
}
|
||||
GameMessagePacket voicePlayersPkt = new SPacketVoiceSignalGlobalEAG(userDatas);
|
||||
for (UserConnection userCon : voicePlayers.values()) {
|
||||
if (!user.equals(userCon.getUniqueId())) {
|
||||
userCon.sendData(VoiceService.CHANNEL, voicePlayersPkt);
|
||||
((EaglerInitialHandler)userCon.getPendingConnection()).sendEaglerMessage(voicePlayersPkt);
|
||||
}
|
||||
}
|
||||
}
|
||||
byte[] userDisconnectPacket = null;
|
||||
Iterator<VoicePair> pairsItr = voicePairs.iterator();
|
||||
while (pairsItr.hasNext()) {
|
||||
VoicePair voicePair = pairsItr.next();
|
||||
@ -229,10 +282,9 @@ public class VoiceServerImpl {
|
||||
if (voicePlayers.size() > 0) {
|
||||
UserConnection conn = voicePlayers.get(target);
|
||||
if (conn != null) {
|
||||
if (userDisconnectPacket == null) {
|
||||
userDisconnectPacket = VoiceSignalPackets.makeVoiceSignalPacketDisconnect(user);
|
||||
}
|
||||
conn.sendData(VoiceService.CHANNEL, userDisconnectPacket);
|
||||
((EaglerInitialHandler) conn.getPendingConnection()).sendEaglerMessage(
|
||||
new SPacketVoiceSignalDisconnectPeerEAG(user.getMostSignificantBits(),
|
||||
user.getLeastSignificantBits()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -240,4 +292,13 @@ public class VoiceServerImpl {
|
||||
}
|
||||
}
|
||||
|
||||
EnumVoiceState getPlayerVoiceState(UUID uniqueId) {
|
||||
synchronized (voicePlayers) {
|
||||
if(voicePlayers.containsKey(uniqueId)) {
|
||||
return EnumVoiceState.ENABLED;
|
||||
}
|
||||
}
|
||||
return EnumVoiceState.DISABLED;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,7 +7,14 @@ import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import gnu.trove.map.TMap;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.server.SPacketRPCEventToggledVoice;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EnumVoiceState;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftVoiceStatusChangeEvent;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerBungeeConfig;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.backend_rpc_protocol.EnumSubscribedEvent;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketVoiceSignalAllowedEAG;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
@ -29,17 +36,15 @@ import net.md_5.bungee.api.config.ServerInfo;
|
||||
*/
|
||||
public class VoiceService {
|
||||
|
||||
public static final String CHANNEL = "EAG|Voice-1.8";
|
||||
|
||||
private final Map<String, VoiceServerImpl> serverMap = new HashMap();
|
||||
private final byte[] disableVoicePacket;
|
||||
private final Map<String, VoiceServerImpl> serverMap = new HashMap<>();
|
||||
private final GameMessagePacket disableVoicePacket;
|
||||
|
||||
public VoiceService(EaglerBungeeConfig conf) {
|
||||
this.disableVoicePacket = VoiceSignalPackets.makeVoiceSignalPacketAllowed(false, null);
|
||||
this.disableVoicePacket = new SPacketVoiceSignalAllowedEAG(false, null);
|
||||
String[] iceServers = conf.getICEServers().toArray(new String[conf.getICEServers().size()]);
|
||||
byte[] iceServersPacket = VoiceSignalPackets.makeVoiceSignalPacketAllowed(true, iceServers);
|
||||
SPacketVoiceSignalAllowedEAG iceServersPacket = new SPacketVoiceSignalAllowedEAG(true, iceServers);
|
||||
TMap<String,ServerInfo> servers = BungeeCord.getInstance().config.getServers();
|
||||
Set<String> keySet = new HashSet(servers.keySet());
|
||||
Set<String> keySet = new HashSet<>(servers.keySet());
|
||||
keySet.removeAll(conf.getDisableVoiceOnServersSet());
|
||||
for(String s : keySet) {
|
||||
serverMap.put(s, new VoiceServerImpl(servers.get(s), iceServersPacket));
|
||||
@ -59,7 +64,12 @@ public class VoiceService {
|
||||
if(svr != null) {
|
||||
svr.handlePlayerLoggedIn(player);
|
||||
}else {
|
||||
player.sendData(CHANNEL, disableVoicePacket);
|
||||
EaglerInitialHandler eaglerHandler = (EaglerInitialHandler)player.getPendingConnection();
|
||||
eaglerHandler.sendEaglerMessage(disableVoicePacket);
|
||||
eaglerHandler.fireVoiceStateChange(EaglercraftVoiceStatusChangeEvent.EnumVoiceState.SERVER_DISABLE);
|
||||
if(eaglerHandler.getRPCEventSubscribed(EnumSubscribedEvent.TOGGLE_VOICE)) {
|
||||
eaglerHandler.getRPCSessionHandler().handleVoiceStateTransition(SPacketRPCEventToggledVoice.VOICE_STATE_SERVER_DISABLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,7 +80,7 @@ public class VoiceService {
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeRequest(UUID player, UserConnection sender) {
|
||||
public void handleVoiceSignalPacketTypeRequest(UUID player, UserConnection sender) {
|
||||
if(sender.getServer() != null) {
|
||||
VoiceServerImpl svr = serverMap.get(sender.getServer().getInfo().getName());
|
||||
if(svr != null) {
|
||||
@ -79,7 +89,7 @@ public class VoiceService {
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeConnect(UserConnection sender) {
|
||||
public void handleVoiceSignalPacketTypeConnect(UserConnection sender) {
|
||||
if(sender.getServer() != null) {
|
||||
VoiceServerImpl svr = serverMap.get(sender.getServer().getInfo().getName());
|
||||
if(svr != null) {
|
||||
@ -88,7 +98,7 @@ public class VoiceService {
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeICE(UUID player, String str, UserConnection sender) {
|
||||
public void handleVoiceSignalPacketTypeICE(UUID player, byte[] str, UserConnection sender) {
|
||||
if(sender.getServer() != null) {
|
||||
VoiceServerImpl svr = serverMap.get(sender.getServer().getInfo().getName());
|
||||
if(svr != null) {
|
||||
@ -97,7 +107,7 @@ public class VoiceService {
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeDesc(UUID player, String str, UserConnection sender) {
|
||||
public void handleVoiceSignalPacketTypeDesc(UUID player, byte[] str, UserConnection sender) {
|
||||
if(sender.getServer() != null) {
|
||||
VoiceServerImpl svr = serverMap.get(sender.getServer().getInfo().getName());
|
||||
if(svr != null) {
|
||||
@ -106,13 +116,31 @@ public class VoiceService {
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeDisconnect(UUID player, UserConnection sender) {
|
||||
public void handleVoiceSignalPacketTypeDisconnect(UserConnection sender) {
|
||||
if(sender.getServer() != null) {
|
||||
VoiceServerImpl svr = serverMap.get(sender.getServer().getInfo().getName());
|
||||
if(svr != null) {
|
||||
svr.handleVoiceSignalPacketTypeDisconnect(player, sender);
|
||||
svr.handleVoiceSignalPacketTypeDisconnect(sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handleVoiceSignalPacketTypeDisconnectPeer(UUID player, UserConnection sender) {
|
||||
if(sender.getServer() != null) {
|
||||
VoiceServerImpl svr = serverMap.get(sender.getServer().getInfo().getName());
|
||||
if(svr != null) {
|
||||
svr.handleVoiceSignalPacketTypeDisconnectPeer(player, sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public EnumVoiceState getPlayerVoiceState(UUID player, ServerInfo info) {
|
||||
VoiceServerImpl svr = serverMap.get(info.getName());
|
||||
if(svr != null) {
|
||||
return svr.getPlayerVoiceState(player);
|
||||
}else {
|
||||
return EnumVoiceState.SERVER_DISABLE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,194 +0,0 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.voice;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class VoiceSignalPackets {
|
||||
|
||||
static final int VOICE_SIGNAL_ALLOWED = 0;
|
||||
static final int VOICE_SIGNAL_REQUEST = 0;
|
||||
static final int VOICE_SIGNAL_CONNECT = 1;
|
||||
static final int VOICE_SIGNAL_DISCONNECT = 2;
|
||||
static final int VOICE_SIGNAL_ICE = 3;
|
||||
static final int VOICE_SIGNAL_DESC = 4;
|
||||
static final int VOICE_SIGNAL_GLOBAL = 5;
|
||||
|
||||
public static void processPacket(byte[] data, UserConnection sender, VoiceService voiceService) throws IOException {
|
||||
int packetId = -1;
|
||||
if(data.length == 0) {
|
||||
throw new IOException("Zero-length packet recieved");
|
||||
}
|
||||
try {
|
||||
ByteBuf buffer = Unpooled.wrappedBuffer(data).writerIndex(data.length);
|
||||
packetId = buffer.readUnsignedByte();
|
||||
switch(packetId) {
|
||||
case VOICE_SIGNAL_REQUEST: {
|
||||
voiceService.handleVoiceSignalPacketTypeRequest(DefinedPacket.readUUID(buffer), sender);
|
||||
break;
|
||||
}
|
||||
case VOICE_SIGNAL_CONNECT: {
|
||||
voiceService.handleVoiceSignalPacketTypeConnect(sender);
|
||||
break;
|
||||
}
|
||||
case VOICE_SIGNAL_ICE: {
|
||||
voiceService.handleVoiceSignalPacketTypeICE(DefinedPacket.readUUID(buffer), DefinedPacket.readString(buffer, 32767), sender);
|
||||
break;
|
||||
}
|
||||
case VOICE_SIGNAL_DESC: {
|
||||
voiceService.handleVoiceSignalPacketTypeDesc(DefinedPacket.readUUID(buffer), DefinedPacket.readString(buffer, 32767), sender);
|
||||
break;
|
||||
}
|
||||
case VOICE_SIGNAL_DISCONNECT: {
|
||||
voiceService.handleVoiceSignalPacketTypeDisconnect(buffer.readableBytes() > 0 ? DefinedPacket.readUUID(buffer) : null, sender);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new IOException("Unknown packet type " + packetId);
|
||||
}
|
||||
}
|
||||
if(buffer.readableBytes() > 0) {
|
||||
throw new IOException("Voice packet is too long!");
|
||||
}
|
||||
}catch(IOException ex) {
|
||||
throw ex;
|
||||
}catch(Throwable t) {
|
||||
throw new IOException("Unhandled exception handling voice packet type " + packetId, t);
|
||||
}
|
||||
}
|
||||
|
||||
static byte[] makeVoiceSignalPacketAllowed(boolean allowed, String[] iceServers) {
|
||||
if (iceServers == null) {
|
||||
byte[] ret = new byte[2];
|
||||
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
|
||||
wrappedBuffer.writeByte(VOICE_SIGNAL_ALLOWED);
|
||||
wrappedBuffer.writeBoolean(allowed);
|
||||
return ret;
|
||||
}
|
||||
byte[][] iceServersBytes = new byte[iceServers.length][];
|
||||
int totalLen = 2 + getVarIntSize(iceServers.length);
|
||||
for(int i = 0; i < iceServers.length; ++i) {
|
||||
byte[] b = iceServersBytes[i] = iceServers[i].getBytes(StandardCharsets.UTF_8);
|
||||
totalLen += getVarIntSize(b.length) + b.length;
|
||||
}
|
||||
byte[] ret = new byte[totalLen];
|
||||
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
|
||||
wrappedBuffer.writeByte(VOICE_SIGNAL_ALLOWED);
|
||||
wrappedBuffer.writeBoolean(allowed);
|
||||
DefinedPacket.writeVarInt(iceServersBytes.length, wrappedBuffer);
|
||||
for(int i = 0; i < iceServersBytes.length; ++i) {
|
||||
byte[] b = iceServersBytes[i];
|
||||
DefinedPacket.writeVarInt(b.length, wrappedBuffer);
|
||||
wrappedBuffer.writeBytes(b);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static byte[] makeVoiceSignalPacketGlobal(Collection<UserConnection> users) {
|
||||
int cnt = users.size();
|
||||
byte[][] displayNames = new byte[cnt][];
|
||||
int i = 0;
|
||||
for(UserConnection user : users) {
|
||||
String name = user.getDisplayName();
|
||||
if(name.length() > 16) name = name.substring(0, 16);
|
||||
displayNames[i++] = name.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
int totalLength = 1 + getVarIntSize(cnt) + (cnt << 4);
|
||||
for(i = 0; i < cnt; ++i) {
|
||||
totalLength += getVarIntSize(displayNames[i].length) + displayNames[i].length;
|
||||
}
|
||||
byte[] ret = new byte[totalLength];
|
||||
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
|
||||
wrappedBuffer.writeByte(VOICE_SIGNAL_GLOBAL);
|
||||
DefinedPacket.writeVarInt(cnt, wrappedBuffer);
|
||||
for(UserConnection user : users) {
|
||||
DefinedPacket.writeUUID(user.getUniqueId(), wrappedBuffer);
|
||||
}
|
||||
for(i = 0; i < cnt; ++i) {
|
||||
DefinedPacket.writeVarInt(displayNames[i].length, wrappedBuffer);
|
||||
wrappedBuffer.writeBytes(displayNames[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static byte[] makeVoiceSignalPacketConnect(UUID player, boolean offer) {
|
||||
byte[] ret = new byte[18];
|
||||
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
|
||||
wrappedBuffer.writeByte(VOICE_SIGNAL_CONNECT);
|
||||
DefinedPacket.writeUUID(player, wrappedBuffer);
|
||||
wrappedBuffer.writeBoolean(offer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static byte[] makeVoiceSignalPacketConnectAnnounce(UUID player) {
|
||||
byte[] ret = new byte[17];
|
||||
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
|
||||
wrappedBuffer.writeByte(VOICE_SIGNAL_CONNECT);
|
||||
DefinedPacket.writeUUID(player, wrappedBuffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static byte[] makeVoiceSignalPacketDisconnect(UUID player) {
|
||||
if(player == null) {
|
||||
return new byte[] { (byte)VOICE_SIGNAL_DISCONNECT };
|
||||
}
|
||||
byte[] ret = new byte[17];
|
||||
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
|
||||
wrappedBuffer.writeByte(VOICE_SIGNAL_DISCONNECT);
|
||||
DefinedPacket.writeUUID(player, wrappedBuffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static byte[] makeVoiceSignalPacketICE(UUID player, String str) {
|
||||
byte[] strBytes = str.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] ret = new byte[17 + getVarIntSize(strBytes.length) + strBytes.length];
|
||||
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
|
||||
wrappedBuffer.writeByte(VOICE_SIGNAL_ICE);
|
||||
DefinedPacket.writeUUID(player, wrappedBuffer);
|
||||
DefinedPacket.writeVarInt(strBytes.length, wrappedBuffer);
|
||||
wrappedBuffer.writeBytes(strBytes);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static byte[] makeVoiceSignalPacketDesc(UUID player, String str) {
|
||||
byte[] strBytes = str.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] ret = new byte[17 + getVarIntSize(strBytes.length) + strBytes.length];
|
||||
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
|
||||
wrappedBuffer.writeByte(VOICE_SIGNAL_DESC);
|
||||
DefinedPacket.writeUUID(player, wrappedBuffer);
|
||||
DefinedPacket.writeVarInt(strBytes.length, wrappedBuffer);
|
||||
wrappedBuffer.writeBytes(strBytes);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static int getVarIntSize(int input) {
|
||||
for (int i = 1; i < 5; ++i) {
|
||||
if ((input & -1 << i * 7) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 5;
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
voice_stun_servers:
|
||||
voice_servers_no_passwd:
|
||||
- 'stun:stun.l.google.com:19302'
|
||||
- 'stun:stun1.l.google.com:19302'
|
||||
- 'stun:stun2.l.google.com:19302'
|
||||
- 'stun:stun3.l.google.com:19302'
|
||||
- 'stun:stun4.l.google.com:19302'
|
||||
- 'stun:openrelay.metered.ca:80'
|
||||
voice_turn_servers:
|
||||
voice_servers_passwd:
|
||||
openrelay1:
|
||||
url: 'turn:openrelay.metered.ca:80'
|
||||
username: 'openrelayproject'
|
||||
|
@ -13,6 +13,10 @@ listener_01:
|
||||
- '&6An EaglercraftX server'
|
||||
allow_motd: true
|
||||
allow_query: true
|
||||
allow_protocol_v3: true
|
||||
allow_protocol_v4: true
|
||||
protocol_v4_defrag_send_delay: 10
|
||||
allow_cookie_revoke_query: true
|
||||
request_motd_cache:
|
||||
cache_ttl: 7200
|
||||
online_server_list_animation: false
|
||||
|
@ -0,0 +1,68 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Eaglercraft Server</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
margin: 16px;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<script type="text/javascript">
|
||||
{% embed text `message_api_v1.js` %}
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
|
||||
// Open the channel, this can be any string
|
||||
serverMessageAPI.openChannel("com.example.test_channel");
|
||||
|
||||
// Set the callback for when messages are recieved
|
||||
serverMessageAPI.addEventListener("message", function(msg) {
|
||||
var newElement = document.createElement("li");
|
||||
if(msg.type === "binary") {
|
||||
newElement.innerText = "[" + msg.channel + "][binary] ArrayBuffer(" + msg.data.byteLength + ")";
|
||||
}else if(msg.type === "string") {
|
||||
newElement.innerText = "[" + msg.channel + "][string] \"" + msg.data + "\"";
|
||||
}
|
||||
document.getElementById("messages_recieved").appendChild(newElement);
|
||||
});
|
||||
|
||||
window.addEventListener("load", function() {
|
||||
document.getElementById("message_send").addEventListener("click", function() {
|
||||
var el = document.getElementById("message_contents");
|
||||
var toSend = el.value.trim();
|
||||
if(toSend.length > 0) {
|
||||
|
||||
// Send the message, can be a string, ArrayBuffer, Int8Array, or Uint8Array
|
||||
serverMessageAPI.send("com.example.test_channel", toSend);
|
||||
|
||||
el.value = "";
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* // Add this event listener to your bungee plugin:
|
||||
*
|
||||
* @EventHandler
|
||||
* public void testWebViewMessageAPI(EaglercraftWebViewMessageEvent event) {
|
||||
* if(event.getType() == MessageType.STRING && event.getChannelName().equals("com.example.test_channel")) {
|
||||
* event.sendResponse(event.getAsString());
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
|
||||
</script>
|
||||
<body>
|
||||
<h1>Message API Test</h1>
|
||||
<h4>Server Version: {% global `plugin_name` %} {% global `plugin_version` %}</h4>
|
||||
<h4>Make sure you enable javascript in "pause_menu.yml"</h4>
|
||||
<p>Message: <input type="text" id="message_contents" placeholder="eagler"> <button id="message_send">Send</button></p>
|
||||
<p>Recieved from server:</p>
|
||||
<ul id="messages_recieved"></ul>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,63 @@
|
||||
"use strict";
|
||||
window.serverMessageAPI = (function() {
|
||||
var channelOpen = null;
|
||||
var messageHandlers = [];
|
||||
window.addEventListener("message", function(evt) {
|
||||
var dat = evt.data;
|
||||
if((typeof dat === "object") && dat.ver === 1 && (typeof dat.type === "string") && (typeof dat.channel === "string") && dat.channel.length > 0) {
|
||||
for(var i = 0; i < messageHandlers.length; ++i) {
|
||||
messageHandlers[i](dat);
|
||||
}
|
||||
}
|
||||
});
|
||||
var ServerMessageAPIError = function(message) {
|
||||
this.name = "ServerMessageAPIError";
|
||||
this.message = message;
|
||||
};
|
||||
ServerMessageAPIError.prototype = Error.prototype;
|
||||
var openCh = function(chName) {
|
||||
if(channelOpen !== null) throw new ServerMessageAPIError("Cannot open multiple channels, this feature is not supported!");
|
||||
channelOpen = chName;
|
||||
window.parent.postMessage({ver:1,channel:chName,open:true}, "*");
|
||||
};
|
||||
var closeCh = function(chName) {
|
||||
if(channelOpen !== chName) throw new ServerMessageAPIError("Cannot close channel \"" + chName + "\", that channel is not open!");
|
||||
channelOpen = null;
|
||||
window.parent.postMessage({ver:1,channel:chName,open:false}, "*");
|
||||
};
|
||||
var addListener = function(name, handler) {
|
||||
if(name === "message") messageHandlers.push(handler);
|
||||
};
|
||||
var remListener = function(name, handler) {
|
||||
if(name === "message") messageHandlers = messageHandlers.filter(function(o) { return o !== handler; });
|
||||
};
|
||||
var fixTypedArray = function(arr) {
|
||||
if(arr.length === arr.buffer.byteLength) {
|
||||
return arr.buffer;
|
||||
}else {
|
||||
var toSend = (data instanceof Uint8Array) ? new Uint8Array(arr.length) : new Int8Array(arr.length);
|
||||
toSend.set(arr);
|
||||
return toSend.buffer;
|
||||
}
|
||||
};
|
||||
var send = function(chName, data) {
|
||||
if(channelOpen !== chName) throw new ServerMessageAPIError("Cannot send message on channel \"" + chName + "\", that channel is not open!");
|
||||
if(typeof data === "string") {
|
||||
window.parent.postMessage({ver:1,channel:chName,data:data}, "*");
|
||||
}else if(data instanceof ArrayBuffer) {
|
||||
window.parent.postMessage({ver:1,channel:chName,data:data}, "*");
|
||||
}else if((data instanceof Uint8Array) || (data instanceof Int8Array)) {
|
||||
window.parent.postMessage({ver:1,channel:chName,data:fixTypedArray(data)}, "*");
|
||||
}else {
|
||||
throw new ServerMessageAPIError("Only strings, ArrayBuffers, Uint8Arrays, and Int8Arrays can be sent with this function!");
|
||||
}
|
||||
};
|
||||
return {
|
||||
ServerMessageAPIError: ServerMessageAPIError,
|
||||
openChannel: openCh,
|
||||
closeChannel: closeCh,
|
||||
addEventListener: addListener,
|
||||
removeEventListener: remListener,
|
||||
send: send
|
||||
};
|
||||
})();
|
@ -0,0 +1,43 @@
|
||||
enable_custom_pause_menu: false
|
||||
server_info_button:
|
||||
enable_button: true
|
||||
button_text: 'Server Info'
|
||||
button_mode_open_new_tab: false
|
||||
server_info_embed_url: ''
|
||||
button_mode_embed_file: true
|
||||
server_info_embed_file: 'server_info.html'
|
||||
server_info_embed_screen_title: 'Server Info'
|
||||
server_info_embed_send_chunk_rate: 1
|
||||
server_info_embed_send_chunk_size: 24576
|
||||
enable_template_macros: true
|
||||
server_info_embed_template_globals:
|
||||
example_global: 'eagler'
|
||||
allow_embed_template_eval_macro: false
|
||||
enable_webview_javascript: false
|
||||
enable_webview_message_api: false
|
||||
enable_webview_strict_csp: true
|
||||
discord_button:
|
||||
enable_button: true
|
||||
button_text: 'Discord'
|
||||
button_url: 'https://invite url here'
|
||||
custom_images:
|
||||
icon_title_L: ''
|
||||
icon_title_R: ''
|
||||
icon_backToGame_L: ''
|
||||
icon_backToGame_R: ''
|
||||
icon_achievements_L: ''
|
||||
icon_achievements_R: ''
|
||||
icon_statistics_L: ''
|
||||
icon_statistics_R: ''
|
||||
icon_serverInfo_L: ''
|
||||
icon_serverInfo_R: ''
|
||||
icon_options_L: ''
|
||||
icon_options_R: ''
|
||||
icon_discord_L: ''
|
||||
icon_discord_R: ''
|
||||
icon_disconnect_L: ''
|
||||
icon_disconnect_R: ''
|
||||
icon_background_pause: 'test_image.png'
|
||||
icon_background_all: 'test_image.png'
|
||||
icon_watermark_pause: ''
|
||||
icon_watermark_all: ''
|
@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Eaglercraft Server</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
margin: 16px;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Hello World</h1>
|
||||
|
||||
{% htmlescape on %}
|
||||
<p>Server Name: {% global `server_name` %}</p>
|
||||
<p>Using: {% global `plugin_name` %} {% global `plugin_version` %}</p>
|
||||
<p>JVM: {% property `java.vm.name` `(unknown)` %} ({% property `java.vm.info` `null` %}) {% property `java.vm.vendor` `(unknown)` %}</p>
|
||||
{% htmlescape off %}
|
||||
|
||||
<p><img src="data:image/png;base64,{% embed base64 `test_image.png` %}" /></p>
|
||||
|
||||
<!-- Note: JPEGs are recommended for larger images to reduce their size -->
|
||||
<!-- <p><img src="data:image/jpeg;base64,(% embed base64 `large_image.jpg` %)" /></p> -->
|
||||
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
@ -23,4 +23,5 @@ eagler_players_vanilla_skin: ''
|
||||
enable_is_eagler_player_property: true
|
||||
disable_voice_chat_on_servers: []
|
||||
disable_fnaw_skins_everywhere: false
|
||||
disable_fnaw_skins_on_servers: []
|
||||
disable_fnaw_skins_on_servers: []
|
||||
enable_backend_rpc_api: false
|
@ -1,5 +1,5 @@
|
||||
name: EaglercraftXBungee
|
||||
main: net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee
|
||||
version: 1.2.7
|
||||
version: 1.3.0
|
||||
description: Plugin to allow EaglercraftX 1.8 players to join your network, or allow EaglercraftX 1.8 players to use your network as a proxy to join other networks
|
||||
author: lax1dude
|
Reference in New Issue
Block a user