diff --git a/forge-core/pom.xml b/forge-core/pom.xml index 5daa8ffcee7..c857b47ab1b 100644 --- a/forge-core/pom.xml +++ b/forge-core/pom.xml @@ -21,7 +21,7 @@ org.apache.commons commons-lang3 - 3.7 + 3.8.1 diff --git a/forge-gui-android/pom.xml b/forge-gui-android/pom.xml index ebc8f35f90f..1101ecc33a4 100644 --- a/forge-gui-android/pom.xml +++ b/forge-gui-android/pom.xml @@ -81,17 +81,17 @@ com.google.guava guava - 24.1-android + 28.1-android com.thoughtworks.xstream xstream - 1.4.7 + 1.4.9 org.apache.commons commons-lang3 - 3.7 + 3.8.1 xmlpull diff --git a/forge-gui-android/proguard.cfg b/forge-gui-android/proguard.cfg index 159c6a4348c..7a8ab24db32 100644 --- a/forge-gui-android/proguard.cfg +++ b/forge-gui-android/proguard.cfg @@ -48,6 +48,7 @@ -keep class com.google.guava.** { *; } -keep class com.google.common.** { *; } -keep class io.sentry.event.Event { *; } +-keep class io.netty.util.internal.logging.** { *; } -keepclassmembers class com.badlogic.gdx.backends.android.AndroidInput* { (com.badlogic.gdx.Application, android.content.Context, java.lang.Object, com.badlogic.gdx.backends.android.AndroidApplicationConfiguration); diff --git a/forge-gui-android/src/forge/app/Main.java b/forge-gui-android/src/forge/app/Main.java index 66728960885..70aa9389704 100644 --- a/forge-gui-android/src/forge/app/Main.java +++ b/forge-gui-android/src/forge/app/Main.java @@ -14,6 +14,7 @@ import android.graphics.BitmapFactory; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.PowerManager; @@ -71,7 +72,8 @@ public class Main extends AndroidApplication { Main.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } - initialize(Forge.getApp(new AndroidClipboard(), adapter, assetsDir)); + boolean value = Build.VERSION.SDK_INT >= 26; + initialize(Forge.getApp(new AndroidClipboard(), adapter, assetsDir, value)); } /*@Override diff --git a/forge-gui-desktop/pom.xml b/forge-gui-desktop/pom.xml index a37c5e7a7b7..3abbf4f47ae 100644 --- a/forge-gui-desktop/pom.xml +++ b/forge-gui-desktop/pom.xml @@ -142,12 +142,12 @@ com.google.guava guava - 24.1-android + 28.1-android com.thoughtworks.xstream xstream - 1.4.10 + 1.4.11.1 org.testng @@ -180,7 +180,7 @@ org.apache.commons commons-lang3 - 3.7 + 3.8.1 org.freemarker diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java b/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java index 5a58fa25598..cc24940921c 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java @@ -3,6 +3,7 @@ package forge.screens.home; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import forge.GuiBase; import forge.UiCommand; import forge.ai.AIOption; import forge.deck.*; @@ -258,6 +259,9 @@ public class VLobby implements ILobbyView { addPlayerBtn.setEnabled(activePlayersNum < MAX_PLAYERS); final boolean allowNetworking = lobby.isAllowNetworking(); + + GuiBase.setNetworkplay(allowNetworking); + ImmutableList vntBoxes = null; if (allowNetworking) { vntBoxes = vntBoxesNetwork; diff --git a/forge-gui-desktop/src/main/java/forge/view/Main.java b/forge-gui-desktop/src/main/java/forge/view/Main.java index 143b21506b2..c49f69764a7 100644 --- a/forge-gui-desktop/src/main/java/forge/view/Main.java +++ b/forge-gui-desktop/src/main/java/forge/view/Main.java @@ -49,6 +49,9 @@ public final class Main { //setup GUI interface GuiBase.setInterface(new GuiDesktop()); + //set PropertyConfig log4j to true + GuiBase.setpropertyConfig(true); + //install our error handler ExceptionHandler.registerErrorHandling(); diff --git a/forge-gui-ios/src/forge/ios/Main.java b/forge-gui-ios/src/forge/ios/Main.java index bad9865e574..689e07d6fa7 100644 --- a/forge-gui-ios/src/forge/ios/Main.java +++ b/forge-gui-ios/src/forge/ios/Main.java @@ -29,7 +29,7 @@ public class Main extends IOSApplication.Delegate { final IOSApplicationConfiguration config = new IOSApplicationConfiguration(); config.useAccelerometer = false; config.useCompass = false; - final ApplicationListener app = Forge.getApp(new IOSClipboard(), new IOSAdapter(), assetsDir); + final ApplicationListener app = Forge.getApp(new IOSClipboard(), new IOSAdapter(), assetsDir, false); final IOSApplication iosApp = new IOSApplication(app, config); return iosApp; } diff --git a/forge-gui-mobile-dev/src/forge/app/Main.java b/forge-gui-mobile-dev/src/forge/app/Main.java index d80bc49cdae..8f2de339592 100644 --- a/forge-gui-mobile-dev/src/forge/app/Main.java +++ b/forge-gui-mobile-dev/src/forge/app/Main.java @@ -93,7 +93,7 @@ public class Main { config.useHDPI = desktopMode; // enable HiDPI on Mac OS new LwjglApplication(Forge.getApp(new LwjglClipboard(), new DesktopAdapter(switchOrientationFile), - desktopMode ? desktopModeAssetsDir : assetsDir), config); + desktopMode ? desktopModeAssetsDir : assetsDir, true), config); } private static class DesktopAdapter implements IDeviceAdapter { diff --git a/forge-gui-mobile/pom.xml b/forge-gui-mobile/pom.xml index f21a549db0e..f3fd4f4f311 100644 --- a/forge-gui-mobile/pom.xml +++ b/forge-gui-mobile/pom.xml @@ -48,17 +48,17 @@ com.google.guava guava - 24.1-android + 28.1-android com.thoughtworks.xstream xstream - 1.4.7 + 1.4.9 org.apache.commons commons-lang3 - 3.7 + 3.8.1 com.badlogicgames.gdx diff --git a/forge-gui-mobile/src/forge/Forge.java b/forge-gui-mobile/src/forge/Forge.java index fef11349ffc..85e5ba365ea 100644 --- a/forge-gui-mobile/src/forge/Forge.java +++ b/forge-gui-mobile/src/forge/Forge.java @@ -67,11 +67,12 @@ public class Forge implements ApplicationListener { public static boolean enablePreloadExtendedArt = false; public static String locale = "en-US"; - public static ApplicationListener getApp(Clipboard clipboard0, IDeviceAdapter deviceAdapter0, String assetDir0) { + public static ApplicationListener getApp(Clipboard clipboard0, IDeviceAdapter deviceAdapter0, String assetDir0, boolean value) { if (GuiBase.getInterface() == null) { clipboard = clipboard0; deviceAdapter = deviceAdapter0; GuiBase.setInterface(new GuiMobile(assetDir0)); + GuiBase.setpropertyConfig(value); } return app; } diff --git a/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java b/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java index 46b5f53cf11..e196ac77237 100644 --- a/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java +++ b/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java @@ -4,6 +4,7 @@ import java.util.Arrays; import java.util.ArrayList; import java.util.List; +import forge.GuiBase; import forge.deck.CardPool; import forge.deck.Deck; import forge.deck.DeckSection; @@ -525,6 +526,9 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView { updateVariantSelection(); final boolean allowNetworking = lobby.isAllowNetworking(); + + GuiBase.setNetworkplay(allowNetworking); + for (int i = 0; i < cbPlayerCount.getSelectedItem(); i++) { final boolean hasPanel = i < playerPanels.size(); if (i < playerCount) { diff --git a/forge-gui-mobile/src/forge/screens/match/winlose/ControlWinLose.java b/forge-gui-mobile/src/forge/screens/match/winlose/ControlWinLose.java index bd01ad00cd7..6cc7a757de3 100644 --- a/forge-gui-mobile/src/forge/screens/match/winlose/ControlWinLose.java +++ b/forge-gui-mobile/src/forge/screens/match/winlose/ControlWinLose.java @@ -53,21 +53,24 @@ public class ControlWinLose { view.hide(); saveOptions(); - MatchController.getHostedMatch().continueMatch(); + try { MatchController.getHostedMatch().continueMatch(); + } catch (NullPointerException e) {} } /** Action performed when "restart" button is pressed in default win/lose UI. */ public void actionOnRestart() { view.hide(); saveOptions(); - MatchController.getHostedMatch().restartMatch(); + try { MatchController.getHostedMatch().restartMatch(); + } catch (NullPointerException e) {} } /** Action performed when "quit" button is pressed in default win/lose UI. */ public void actionOnQuit() { // Reset other stuff saveOptions(); - MatchController.getHostedMatch().endCurrentGame(); + try { MatchController.getHostedMatch().endCurrentGame(); + } catch (NullPointerException e) {} view.hide(); } diff --git a/forge-gui/pom.xml b/forge-gui/pom.xml index 3549e6bda43..53d3f3302d3 100644 --- a/forge-gui/pom.xml +++ b/forge-gui/pom.xml @@ -39,22 +39,22 @@ com.google.guava guava - 24.1-android + 28.1-android com.thoughtworks.xstream xstream - 1.4.10 + 1.4.11.1 org.apache.commons commons-lang3 - 3.7 + 3.8.1 io.netty netty-all - 4.0.25.Final + 4.1.43.Final compile @@ -72,6 +72,11 @@ slf4j-simple 1.7.22 + + org.mapdb + elsa + 3.0.0-M7 + diff --git a/forge-gui/src/main/java/forge/FThreads.java b/forge-gui/src/main/java/forge/FThreads.java index 95f3df74158..e065cf85079 100644 --- a/forge-gui/src/main/java/forge/FThreads.java +++ b/forge-gui/src/main/java/forge/FThreads.java @@ -13,6 +13,8 @@ public class FThreads { * @param mustBeEDT   boolean: true = exception if not EDT, false = exception if EDT */ public static void assertExecutedByEdt(final boolean mustBeEDT) { + if (GuiBase.isNetworkplay()) + return; //don't check for networkplay if (isGuiThread() != mustBeEDT) { final StackTraceElement[] trace = Thread.currentThread().getStackTrace(); final String methodName = trace[2].getClassName() + "." + trace[2].getMethodName(); diff --git a/forge-gui/src/main/java/forge/GuiBase.java b/forge-gui/src/main/java/forge/GuiBase.java index a675dfbdd73..13fb19043de 100644 --- a/forge-gui/src/main/java/forge/GuiBase.java +++ b/forge-gui/src/main/java/forge/GuiBase.java @@ -4,6 +4,8 @@ import forge.interfaces.IGuiBase; public class GuiBase { private static IGuiBase guiInterface; + private static boolean propertyConfig = true; + private static boolean networkplay = false; public static IGuiBase getInterface() { return guiInterface; @@ -11,4 +13,16 @@ public class GuiBase { public static void setInterface(IGuiBase i0) { guiInterface = i0; } + public static void setpropertyConfig(boolean value) { + propertyConfig = value; + } + public static boolean isNetworkplay() { + return networkplay; + } + public static void setNetworkplay(boolean value) { + networkplay = value; + } + public static boolean getpropertyConfig() { + return propertyConfig; + } } diff --git a/forge-gui/src/main/java/forge/net/CustomObjectDecoder.java b/forge-gui/src/main/java/forge/net/CustomObjectDecoder.java new file mode 100644 index 00000000000..b27ff3d4258 --- /dev/null +++ b/forge-gui/src/main/java/forge/net/CustomObjectDecoder.java @@ -0,0 +1,41 @@ +package forge.net; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import io.netty.handler.codec.serialization.ClassResolver; +import org.mapdb.elsa.ElsaObjectInputStream; + +public class CustomObjectDecoder extends LengthFieldBasedFrameDecoder { + private final ClassResolver classResolver; + + public CustomObjectDecoder(ClassResolver classResolver) { + this(1048576, classResolver); + } + + public CustomObjectDecoder(int maxObjectSize, ClassResolver classResolver) { + super(maxObjectSize, 0, 4, 0, 4); + this.classResolver = classResolver; + } + + protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + ByteBuf frame = (ByteBuf) super.decode(ctx, in); + if (frame == null) { + return null; + } else { + ElsaObjectInputStream ois = new ElsaObjectInputStream(new ByteBufInputStream(frame, true)); + + Object var5; + try { + var5 = ois.readObject(); + } finally { + ois.close(); + } + + return var5; + } + } + + public static int maxObjectsize = 10000000; //10megabyte??? +} diff --git a/forge-gui/src/main/java/forge/net/CustomObjectEncoder.java b/forge-gui/src/main/java/forge/net/CustomObjectEncoder.java new file mode 100644 index 00000000000..df2afec2d49 --- /dev/null +++ b/forge-gui/src/main/java/forge/net/CustomObjectEncoder.java @@ -0,0 +1,39 @@ +package forge.net; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufOutputStream; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import org.mapdb.elsa.ElsaObjectOutputStream; + +import java.io.Serializable; + +public class CustomObjectEncoder extends MessageToByteEncoder { + private static final byte[] LENGTH_PLACEHOLDER = new byte[4]; + + public CustomObjectEncoder() { + } + + protected void encode(ChannelHandlerContext ctx, Serializable msg, ByteBuf out) throws Exception { + int startIdx = out.writerIndex(); + ByteBufOutputStream bout = new ByteBufOutputStream(out); + ElsaObjectOutputStream oout = null; + + try { + bout.write(LENGTH_PLACEHOLDER); + oout = new ElsaObjectOutputStream(bout); + oout.writeObject(msg); + oout.flush(); + } finally { + if (oout != null) { + oout.close(); + } else { + bout.close(); + } + + } + + int endIdx = out.writerIndex(); + out.setInt(startIdx, endIdx - startIdx - 4); + } +} diff --git a/forge-gui/src/main/java/forge/net/GameProtocolHandler.java b/forge-gui/src/main/java/forge/net/GameProtocolHandler.java index face68c68ac..e861f2ab5cc 100644 --- a/forge-gui/src/main/java/forge/net/GameProtocolHandler.java +++ b/forge-gui/src/main/java/forge/net/GameProtocolHandler.java @@ -40,7 +40,7 @@ public abstract class GameProtocolHandler extends ChannelInboundHandlerAdapte } final Object[] args = event.getObjects(); - protocolMethod.checkArgs(args); + //protocolMethod.checkArgs(args); final Object toInvoke = getToInvoke(ctx); diff --git a/forge-gui/src/main/java/forge/net/GameProtocolSender.java b/forge-gui/src/main/java/forge/net/GameProtocolSender.java index c692ebcb5c8..028b508a86f 100644 --- a/forge-gui/src/main/java/forge/net/GameProtocolSender.java +++ b/forge-gui/src/main/java/forge/net/GameProtocolSender.java @@ -12,13 +12,13 @@ public final class GameProtocolSender { } public void send(final ProtocolMethod method, final Object... args) { - method.checkArgs(args); + //method.checkArgs(args); remote.send(new GuiGameEvent(method, args)); } @SuppressWarnings("unchecked") public T sendAndWait(final ProtocolMethod method, final Object... args) { - method.checkArgs(args); + //method.checkArgs(args); try { final Object returned = remote.sendAndWait(new GuiGameEvent(method, args)); method.checkReturnValue(returned); diff --git a/forge-gui/src/main/java/forge/net/ProtocolMethod.java b/forge-gui/src/main/java/forge/net/ProtocolMethod.java index e220668832f..e7f52516f3b 100644 --- a/forge-gui/src/main/java/forge/net/ProtocolMethod.java +++ b/forge-gui/src/main/java/forge/net/ProtocolMethod.java @@ -65,7 +65,7 @@ public enum ProtocolMethod { confirm (Mode.SERVER, Boolean.TYPE, CardView.class, String.class, Boolean.TYPE, List/*String*/.class), getChoices (Mode.SERVER, List.class, String.class, Integer.TYPE, Integer.TYPE, List.class, Object.class, Function.class), order (Mode.SERVER, List.class, String.class, String.class, Integer.TYPE, Integer.TYPE, List.class, List.class, CardView.class, Boolean.TYPE), - sideboard (Mode.SERVER, List.class, CardPool.class, CardPool.class), + sideboard (Mode.SERVER, List.class, CardPool.class, CardPool.class, String.class), chooseSingleEntityForEffect(Mode.SERVER, GameEntityView.class, String.class, List.class, DelayedReveal.class, Boolean.TYPE), chooseEntitiesForEffect(Mode.SERVER, GameEntityView.class, String.class, List.class, Integer.TYPE, Integer.TYPE, DelayedReveal.class), manipulateCardList (Mode.SERVER, List.class, String.class, Iterable.class, Iterable.class, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE), @@ -156,10 +156,19 @@ public enum ProtocolMethod { public void checkArgs(final Object[] args) { for (int iArg = 0; iArg < args.length; iArg++) { - final Object arg = args[iArg]; - final Class type = this.args[iArg]; - if (!ReflectionUtil.isInstance(arg, type)) { - throw new InternalError(String.format("Protocol method %s: illegal argument (%d) of type %s, %s expected", name(), iArg, arg.getClass().getName(), type.getName())); + Object arg = null; + Class type = null; + try { + arg = args[iArg]; + if (this.args.length > iArg) + type = this.args[iArg]; + } + catch (ArrayIndexOutOfBoundsException ex){ ex.printStackTrace(); } + catch(ConcurrentModificationException ex) { ex.printStackTrace(); } + if (arg != null) + if (type != null) + if (!ReflectionUtil.isInstance(arg, type)) { + throw new InternalError(String.format("Protocol method %s: illegal argument (%d) of type %s, %s expected", name(), iArg, arg.getClass().getName(), type.getName())); } if (arg != null) { // attempt to Serialize each argument, this will throw an exception if it can't. diff --git a/forge-gui/src/main/java/forge/net/client/FGameClient.java b/forge-gui/src/main/java/forge/net/client/FGameClient.java index 7a0f563730d..148118df536 100644 --- a/forge-gui/src/main/java/forge/net/client/FGameClient.java +++ b/forge-gui/src/main/java/forge/net/client/FGameClient.java @@ -1,5 +1,7 @@ package forge.net.client; +import forge.net.CustomObjectDecoder; +import forge.net.CustomObjectEncoder; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; @@ -12,8 +14,6 @@ import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.serialization.ClassResolvers; -import io.netty.handler.codec.serialization.ObjectDecoder; -import io.netty.handler.codec.serialization.ObjectEncoder; import java.util.List; import java.util.concurrent.TimeoutException; @@ -58,8 +58,8 @@ public class FGameClient implements IToServer { public void initChannel(final SocketChannel ch) throws Exception { final ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast( - new ObjectEncoder(), - new ObjectDecoder(ClassResolvers.cacheDisabled(null)), + new CustomObjectEncoder(), + new CustomObjectDecoder(CustomObjectDecoder.maxObjectsize, ClassResolvers.cacheDisabled(null)), new MessageHandler(), new LobbyUpdateHandler(), new GameClientHandler(FGameClient.this)); diff --git a/forge-gui/src/main/java/forge/net/server/FServerManager.java b/forge-gui/src/main/java/forge/net/server/FServerManager.java index 48cfe582ec6..08c27102817 100644 --- a/forge-gui/src/main/java/forge/net/server/FServerManager.java +++ b/forge-gui/src/main/java/forge/net/server/FServerManager.java @@ -6,6 +6,8 @@ import forge.interfaces.IGuiGame; import forge.interfaces.ILobbyListener; import forge.match.LobbySlot; import forge.match.LobbySlotType; +import forge.net.CustomObjectDecoder; +import forge.net.CustomObjectEncoder; import forge.net.event.LobbyUpdateEvent; import forge.net.event.LoginEvent; import forge.net.event.LogoutEvent; @@ -25,8 +27,6 @@ import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.serialization.ClassResolvers; -import io.netty.handler.codec.serialization.ObjectDecoder; -import io.netty.handler.codec.serialization.ObjectEncoder; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; @@ -40,6 +40,8 @@ import java.util.Enumeration; import java.util.Map; import org.apache.log4j.PropertyConfigurator; +import io.netty.util.internal.logging.InternalLoggerFactory; +import io.netty.util.internal.logging.Log4JLoggerFactory; import org.fourthline.cling.UpnpService; import org.fourthline.cling.UpnpServiceImpl; import org.fourthline.cling.support.igd.PortMappingListener; @@ -85,7 +87,8 @@ public final class FServerManager { */ public static FServerManager getInstance() { if (instance == null) { - PropertyConfigurator.configure(ForgeConstants.ASSETS_DIR + "/src/main/resources/log4jConfig.config"); + if(GuiBase.getpropertyConfig()) + PropertyConfigurator.configure(ForgeConstants.ASSETS_DIR + "/src/main/resources/log4jConfig.config"); instance = new FServerManager(); } return instance; @@ -93,16 +96,18 @@ public final class FServerManager { public void startServer(final int port) { try { + InternalLoggerFactory.setDefaultFactory(Log4JLoggerFactory.INSTANCE); final ServerBootstrap b = new ServerBootstrap() .group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new ChannelInitializer() { - @Override public final void initChannel(final SocketChannel ch) { + @Override + public final void initChannel(final SocketChannel ch) throws Exception { final ChannelPipeline p = ch.pipeline(); p.addLast( - new ObjectEncoder(), - new ObjectDecoder(ClassResolvers.cacheDisabled(null)), + new CustomObjectEncoder(), + new CustomObjectDecoder(CustomObjectDecoder.maxObjectsize, ClassResolvers.cacheDisabled(null)), new MessageHandler(), new RegisterClientHandler(), new LobbyInputHandler(),