From 5eee0c6de77cf942a2252774c8fb7c8e22f77186 Mon Sep 17 00:00:00 2001 From: BetaSteward Date: Sat, 20 Mar 2010 03:44:53 +0000 Subject: [PATCH] Initial --- Mage.Server/build.xml | 117 +++ Mage.Server/catalog.xml | 14 + Mage.Server/config/config.xml | 12 + Mage.Server/manifest.mf | 3 + Mage.Server/nbproject/build-impl.xml | 713 ++++++++++++++++++ Mage.Server/nbproject/genfiles.properties | 8 + Mage.Server/nbproject/project.properties | 83 ++ Mage.Server/nbproject/project.xml | 62 ++ Mage.Server/nbproject/xml_binding_build.xml | 31 + Mage.Server/nbproject/xml_binding_cfg.xml | 13 + Mage.Server/plugins/Mage.AI.jar | Bin 0 -> 38256 bytes .../plugins/Mage.Game.TwoPlayerDuel.jar | Bin 0 -> 2687 bytes Mage.Server/plugins/Mage.HumanPlayer.jar | Bin 0 -> 15257 bytes Mage.Server/release/startServer.bat | 1 + Mage.Server/src/mage/server/ChatManager.java | 72 ++ Mage.Server/src/mage/server/ChatSession.java | 119 +++ Mage.Server/src/mage/server/Main.java | 80 ++ Mage.Server/src/mage/server/ServerImpl.java | 440 +++++++++++ Mage.Server/src/mage/server/Session.java | 132 ++++ .../src/mage/server/SessionManager.java | 64 ++ .../src/mage/server/game/GameCallback.java | 39 + .../src/mage/server/game/GameController.java | 354 +++++++++ .../src/mage/server/game/GameFactory.java | 82 ++ .../src/mage/server/game/GameManager.java | 125 +++ .../src/mage/server/game/GameSession.java | 262 +++++++ .../src/mage/server/game/GameWatcher.java | 135 ++++ .../src/mage/server/game/GameWorker.java | 64 ++ .../src/mage/server/game/GamesRoom.java | 53 ++ .../src/mage/server/game/GamesRoomImpl.java | 101 +++ .../mage/server/game/GamesRoomManager.java | 72 ++ .../src/mage/server/game/PlayerFactory.java | 79 ++ .../src/mage/server/game/ReplayManager.java | 73 ++ .../src/mage/server/game/ReplaySession.java | 115 +++ Mage.Server/src/mage/server/game/Room.java | 41 + .../src/mage/server/game/RoomImpl.java | 66 ++ .../src/mage/server/game/TableController.java | 156 ++++ .../src/mage/server/game/TableManager.java | 116 +++ Mage.Server/src/mage/server/util/Config.java | 63 ++ Mage.Server/src/mage/server/util/Config.xsd | 48 ++ .../src/mage/server/util/ConfigSettings.java | 90 +++ .../src/mage/server/util/ThreadExecutor.java | 65 ++ .../src/mage/server/util/resources/config.xml | 12 + Mage.Server/src/security.policy | 3 + .../xml-resources/jaxb/Config/Config.xsd | 48 ++ 44 files changed, 4226 insertions(+) create mode 100644 Mage.Server/build.xml create mode 100644 Mage.Server/catalog.xml create mode 100644 Mage.Server/config/config.xml create mode 100644 Mage.Server/manifest.mf create mode 100644 Mage.Server/nbproject/build-impl.xml create mode 100644 Mage.Server/nbproject/genfiles.properties create mode 100644 Mage.Server/nbproject/project.properties create mode 100644 Mage.Server/nbproject/project.xml create mode 100644 Mage.Server/nbproject/xml_binding_build.xml create mode 100644 Mage.Server/nbproject/xml_binding_cfg.xml create mode 100644 Mage.Server/plugins/Mage.AI.jar create mode 100644 Mage.Server/plugins/Mage.Game.TwoPlayerDuel.jar create mode 100644 Mage.Server/plugins/Mage.HumanPlayer.jar create mode 100644 Mage.Server/release/startServer.bat create mode 100644 Mage.Server/src/mage/server/ChatManager.java create mode 100644 Mage.Server/src/mage/server/ChatSession.java create mode 100644 Mage.Server/src/mage/server/Main.java create mode 100644 Mage.Server/src/mage/server/ServerImpl.java create mode 100644 Mage.Server/src/mage/server/Session.java create mode 100644 Mage.Server/src/mage/server/SessionManager.java create mode 100644 Mage.Server/src/mage/server/game/GameCallback.java create mode 100644 Mage.Server/src/mage/server/game/GameController.java create mode 100644 Mage.Server/src/mage/server/game/GameFactory.java create mode 100644 Mage.Server/src/mage/server/game/GameManager.java create mode 100644 Mage.Server/src/mage/server/game/GameSession.java create mode 100644 Mage.Server/src/mage/server/game/GameWatcher.java create mode 100644 Mage.Server/src/mage/server/game/GameWorker.java create mode 100644 Mage.Server/src/mage/server/game/GamesRoom.java create mode 100644 Mage.Server/src/mage/server/game/GamesRoomImpl.java create mode 100644 Mage.Server/src/mage/server/game/GamesRoomManager.java create mode 100644 Mage.Server/src/mage/server/game/PlayerFactory.java create mode 100644 Mage.Server/src/mage/server/game/ReplayManager.java create mode 100644 Mage.Server/src/mage/server/game/ReplaySession.java create mode 100644 Mage.Server/src/mage/server/game/Room.java create mode 100644 Mage.Server/src/mage/server/game/RoomImpl.java create mode 100644 Mage.Server/src/mage/server/game/TableController.java create mode 100644 Mage.Server/src/mage/server/game/TableManager.java create mode 100644 Mage.Server/src/mage/server/util/Config.java create mode 100644 Mage.Server/src/mage/server/util/Config.xsd create mode 100644 Mage.Server/src/mage/server/util/ConfigSettings.java create mode 100644 Mage.Server/src/mage/server/util/ThreadExecutor.java create mode 100644 Mage.Server/src/mage/server/util/resources/config.xml create mode 100644 Mage.Server/src/security.policy create mode 100644 Mage.Server/xml-resources/jaxb/Config/Config.xsd diff --git a/Mage.Server/build.xml b/Mage.Server/build.xml new file mode 100644 index 00000000000..467d482a0f7 --- /dev/null +++ b/Mage.Server/build.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + Builds, tests, and runs the project Mage.Server. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mage.Server/catalog.xml b/Mage.Server/catalog.xml new file mode 100644 index 00000000000..2db64342c3e --- /dev/null +++ b/Mage.Server/catalog.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml new file mode 100644 index 00000000000..5b6dbf522bb --- /dev/null +++ b/Mage.Server/config/config.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/Mage.Server/manifest.mf b/Mage.Server/manifest.mf new file mode 100644 index 00000000000..1574df4a2de --- /dev/null +++ b/Mage.Server/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/Mage.Server/nbproject/build-impl.xml b/Mage.Server/nbproject/build-impl.xml new file mode 100644 index 00000000000..5f9f8012a6b --- /dev/null +++ b/Mage.Server/nbproject/build-impl.xml @@ -0,0 +1,713 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + + + + + + java -cp "${run.classpath.with.dist.jar}" ${main.class} + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mage.Server/nbproject/genfiles.properties b/Mage.Server/nbproject/genfiles.properties new file mode 100644 index 00000000000..d1a8818d3cb --- /dev/null +++ b/Mage.Server/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=0771c912 +build.xml.script.CRC32=7fe29de1 +build.xml.stylesheet.CRC32=958a1d3e@1.26.2.45 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=c70740ce +nbproject/build-impl.xml.script.CRC32=5b9b3297 +nbproject/build-impl.xml.stylesheet.CRC32=5c621a33@1.26.2.45 diff --git a/Mage.Server/nbproject/project.properties b/Mage.Server/nbproject/project.properties new file mode 100644 index 00000000000..810bafcdbdc --- /dev/null +++ b/Mage.Server/nbproject/project.properties @@ -0,0 +1,83 @@ +application.title=MageServer +application.vendor=BetaSteward_at_googlemail.com +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/Mage.Server.jar +dist.javadoc.dir=${dist.dir}/javadoc +excludes= +includes=** +jar.compress=false +javac.classpath=\ + ${reference.Mage.jar}:\ + ${reference.Mage_Common.jar}:\ + ${reference.Mage_Sets.jar}:\ + ${reference.Mage_HumanPlayer.jar}:\ + ${reference.Mage_AI.jar} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.source=1.6 +javac.target=1.6 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir}:\ + ${libs.junit.classpath}:\ + ${libs.junit_4.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +jaxbwiz.endorsed.dirs="${netbeans.home}/../ide9/modules/ext/jaxb/api" +jaxbwiz.gensrc.classpath=${libs.jaxb.classpath} +jaxbwiz.xjcdef.classpath=${libs.jaxb.classpath} +jaxbwiz.xjcrun.classpath=${libs.jaxb.classpath} +main.class=mage.server.Main +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +platform.active=default_platform +project.license=bsd +project.Mage=../Mage +project.Mage_AI=../Mage.AI +project.Mage_Common=../Mage.Common +project.Mage_HumanPlayer=../Mage.HumanPlayer +project.Mage_Sets=../Mage.Sets +reference.Mage.jar=${project.Mage}/dist/Mage.jar +reference.Mage_AI.jar=${project.Mage_AI}/dist/Mage.AI.jar +reference.Mage_Common.jar=${project.Mage_Common}/dist/Mage.Common.jar +reference.Mage_HumanPlayer.jar=${project.Mage_HumanPlayer}/dist/Mage.HumanPlayer.jar +reference.Mage_Sets.jar=${project.Mage_Sets}/dist/Mage.Sets.jar +run-sys-prop.java.endorsed.dirs=${jaxbwiz.endorsed.dirs} +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +run.jvmargs=-server -Djava.security.policy=src/security.policy +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test +app.version=0.1 diff --git a/Mage.Server/nbproject/project.xml b/Mage.Server/nbproject/project.xml new file mode 100644 index 00000000000..b27cd85f4b4 --- /dev/null +++ b/Mage.Server/nbproject/project.xml @@ -0,0 +1,62 @@ + + + org.netbeans.modules.java.j2seproject + + + + + + + + Mage.Server + + + + + + + + + + Mage + jar + + jar + clean + jar + + + Mage_AI + jar + + jar + clean + jar + + + Mage_Common + jar + + jar + clean + jar + + + Mage_HumanPlayer + jar + + jar + clean + jar + + + Mage_Sets + jar + + jar + clean + jar + + + + diff --git a/Mage.Server/nbproject/xml_binding_build.xml b/Mage.Server/nbproject/xml_binding_build.xml new file mode 100644 index 00000000000..ce85c83ccba --- /dev/null +++ b/Mage.Server/nbproject/xml_binding_build.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mage.Server/nbproject/xml_binding_cfg.xml b/Mage.Server/nbproject/xml_binding_cfg.xml new file mode 100644 index 00000000000..e5b5a0afa0a --- /dev/null +++ b/Mage.Server/nbproject/xml_binding_cfg.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Mage.Server/plugins/Mage.AI.jar b/Mage.Server/plugins/Mage.AI.jar new file mode 100644 index 0000000000000000000000000000000000000000..37fa8d3e691c8d0caf951d19afbc1e2d833a7ebc GIT binary patch literal 38256 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

P9o%@nV7n_xq?P4Ry|+1VL+3HbIg| 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%0AR5#6TEWtqv)>5Q;qz zLJouid@E_{ilssh@P+UKP}}O~sR!$lj@IgbS@cmcKUI`g7w4B(mKA*YB9Zsg>G<^c z4@cg!5rz1V_Ym0scJJVIV9uu%#g*k{rB&5aD$2YNk}D6`pJNv7qvB`qrpC;JeZup% z`X7Jt9v&TJ;oUnUo)*GM>|1qoE|90<~?*ZWa^p+mV zlYtC;VAp-)eBdqo4YMKOzI(@#G#3j5wGw{1DK)H>=YMv^iwN-ZUrqw7yRcEhZc!qv z)R2mkHiFr}cJKHBOdK1Mv#xL0O64K8`Vh6`@hh7{o3Mfo-L_`R28k|i(sbD^!T zccFPYPKN{&qwJ{>O`ZsGL4W`BgMfAi@RSHA8-6a-@TIAtrZxjtEDeAsu*C-d-3fkq I=xToZKjQ^3uK)l5 literal 0 HcmV?d00001 diff --git a/Mage.Server/plugins/Mage.Game.TwoPlayerDuel.jar b/Mage.Server/plugins/Mage.Game.TwoPlayerDuel.jar new file mode 100644 index 0000000000000000000000000000000000000000..bb8b8945d080633120bd6726792b8fd379d679d5 GIT binary patch literal 2687 zcmai0TXPd-7=AW~ZoA!|3+VIhZnH@&xS4#1_j{iA^u6D=o81bA6o}_+y|nBM zyrv5wq}TI_Xfm@AfAI{+HlPcH-u!#~$2)|I{}BHTXgZNeZmj3>vGhh-GmSfX)sAlK zR@Eq*=Oc5mGd)VeY?34@T47I*P{zf@*j$gYYU!G-??zYZ#4p6=qdRBjB9pnA8A%%j zt6Z&DZGFEQNt%V&RFBf5_gJ05dSw@aQO%2 zY)Px@)@8C{g_2gSK6~-xDKT@f6pUiA6CWrT!G~RJ&MR2LvVse^D5jSbe1wERt%zn- z;MQcUD^Rg0CL0Pa<4QM@*zCp@Qld%A$aG^6SG$qL$1*;Vk(1D|Y?y|9Q9^KXYFk2R zwY;lKIFT|;JyYA?(XG6;Q=()bRW4|yZOt;o+!KcEJ)_NnUCvn$!vGa?{+2^`cOo zGm55W*DU5};eSIpR)vx)*Q|oRVK|%IvU@SHQz}kkj0M4!h#PQZt)`b$vuvIcAJ^-rP!s~|Q@>Z!O04E#uLPXKb#>>iUa*&@Zt^(kCiRPeT`h#3 zJaV`-rB!-7bWEzr=(w+yYPv`DU05xbO0>hPSx}FY^GF)rd5|#D5@gdk8Stq`QYLs$ zt7i1CdDeL&Fp!YxOj>UdFHmkG0~CpDN{}colC|AkjPqI?<@E~4VEh?QW$ z>Bo?M;UIw5*(&UT(|m(X;th^e7sOlqEzTsojR;$zAj)1S2o3~(X6Mp79MNnOHQ1nz zW5T1(delJpr#9wX6Eo0YzSCefjP?z+Q_nR~eWN^wp3xj#6#kA+A0Qa|4x`h*B6NW8 z0XjI6577B7WG17_Pew0-BrH?$1%`d8$-sESH=c3Nzy=M7N!dOEW z)*VeFF51&1Qh%CB7fGGMG}lCupRyP4;?3Wn-gu0jG}ZPV;KU*NBxIt8=$G&fq8toJ z_#K1iLt~*s3`v-A;_09DriT~~;Q9~${5dQ3D(GzWh@&})PHfWR78a4h6=WRW7PvOW z(F|r;3d5|HC}K`FHXHtKdj57|1#z6Ar*qUT3dWVZPsuzb0W45<))}44Zb^Zm;LE?8 zh~m?S=Lyj_K8@V3D4%-Vca$KA=h4F%whe02SJkVGi!8g3F#ZRJ&VBGIiW`L>3Cdqb q6z95Z=e2(k36^*sJ?v#s`gdsSo5=Y-%5H@_1T@UwJi+I>#Oz-*5HxK7 literal 0 HcmV?d00001 diff --git a/Mage.Server/plugins/Mage.HumanPlayer.jar b/Mage.Server/plugins/Mage.HumanPlayer.jar new file mode 100644 index 0000000000000000000000000000000000000000..52da76d553d3b4f18659c9334a2f7eade012a7a5 GIT binary patch literal 15257 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 chatSessions = new ConcurrentHashMap(); + + public UUID createChatSession() { + ChatSession chatSession = new ChatSession(); + chatSessions.put(chatSession.getChatId(), chatSession); + return chatSession.getChatId(); + } + + public void joinChat(UUID chatId, ChatClient client) { + chatSessions.get(chatId).join(client); + } + + public void leaveChat(UUID chatId, UUID clientId) { + chatSessions.get(chatId).leave(clientId); + } + + public void destroyChatSession(UUID chatId) { + chatSessions.remove(chatId); + } + + public void broadcast(UUID chatId, String userName, String message) { + chatSessions.get(chatId).broadcast(userName, message); + } +} diff --git a/Mage.Server/src/mage/server/ChatSession.java b/Mage.Server/src/mage/server/ChatSession.java new file mode 100644 index 00000000000..6fe7c4559c2 --- /dev/null +++ b/Mage.Server/src/mage/server/ChatSession.java @@ -0,0 +1,119 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server; + +import mage.server.util.ThreadExecutor; +import java.rmi.RemoteException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.Map.Entry; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.logging.Level; +import java.util.logging.Logger; +import mage.interfaces.ChatClient; +import mage.util.Logging; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class ChatSession { + + private static ExecutorService executor = ThreadExecutor.getInstance().getRMIExecutor(); + private final static Logger logger = Logging.getLogger(ChatSession.class.getName()); + private ConcurrentHashMap clients = new ConcurrentHashMap(); + private UUID chatId; + private DateFormat timeFormatter = SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT); + + //TODO: use sessionId for chatting - prevents sending without being part of the chat + + public ChatSession() { + chatId = UUID.randomUUID(); + } + + public void join(ChatClient client) { + try { + logger.log(Level.INFO, "joining chat " + chatId); + clients.put(client.getId(), client); + broadcast(client.getName(), " has joined"); + } catch (RemoteException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + + public void leave(UUID clientId) { + if (clients.contains(clientId)) { + String clientName = ""; + try { + clientName = clients.get(clientId).getName(); + } catch (RemoteException ex) { + logger.log(Level.SEVERE, null, ex); + } + kill(clientId); + broadcast(clientName, " has left"); + } + } + + public void kill(UUID clientId) { + if (clients.contains(clientId)) + clients.remove(clientId); + } + + public void broadcast(final String userName, final String message) { + Calendar cal = new GregorianCalendar(); + final String msg = timeFormatter.format(cal.getTime()) + " " + userName + ":" + message; + for (final Entry entry: clients.entrySet()) { + executor.submit( + new Runnable() { + public void run() { + try { + entry.getValue().receiveMessage(msg); + } + catch (RemoteException ex) { + logger.log(Level.WARNING, ex.getMessage()); + kill(entry.getKey()); + } + } + } + ); + } + } + + /** + * @return the chatId + */ + public UUID getChatId() { + return chatId; + } + +} diff --git a/Mage.Server/src/mage/server/Main.java b/Mage.Server/src/mage/server/Main.java new file mode 100644 index 00000000000..0d977934254 --- /dev/null +++ b/Mage.Server/src/mage/server/Main.java @@ -0,0 +1,80 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server; + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.logging.Level; +import java.util.logging.Logger; +import mage.interfaces.Server; +import mage.server.game.GameFactory; +import mage.server.game.PlayerFactory; +import mage.server.util.ConfigSettings; +import mage.server.util.config.Plugin; +import mage.util.Logging; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class Main { + + private static Logger logger = Logging.getLogger(Main.class.getName()); + + /** + * @param args the command line arguments + */ + public static void main(String[] args) { + + logger.info("Starting MAGE server version " + Main.class.getPackage().getImplementationVersion()); + ConfigSettings config = ConfigSettings.getInstance(); + for (Plugin plugin: config.getGameTypes()) { + GameFactory.getInstance().addGameType(plugin.getName(), loadPlugin(plugin)); + } + for (Plugin plugin: config.getPlayerTypes()) { + PlayerFactory.getInstance().addPlayerType(plugin.getName(), loadPlugin(plugin)); + } + Server server = new ServerImpl(config.getPort(), config.getServerName()); + + } + + private static Class loadPlugin(Plugin plugin) { + try { + File jarFile = new File("plugins/" + plugin.getJar()); + URLClassLoader urlClassLoader = URLClassLoader.newInstance(new URL[] { jarFile.toURI().toURL() }, Main.class.getClassLoader()); + logger.info("Loaded plugin: " + plugin.getClassName()); + return Class.forName(plugin.getClassName(), true, urlClassLoader); + } catch (Exception ex) { + logger.log(Level.SEVERE, "Error loading plugin " + plugin.getJar(), ex); + } + return null; + } + +} diff --git a/Mage.Server/src/mage/server/ServerImpl.java b/Mage.Server/src/mage/server/ServerImpl.java new file mode 100644 index 00000000000..dd256302698 --- /dev/null +++ b/Mage.Server/src/mage/server/ServerImpl.java @@ -0,0 +1,440 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server; + +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; +import java.rmi.server.UnicastRemoteObject; +import java.util.Collection; +import java.util.List; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; +import mage.Constants.DeckType; +import mage.interfaces.ChatClient; +import mage.interfaces.Client; +import mage.cards.decks.DeckCardLists; +import mage.interfaces.GameClient; +import mage.interfaces.GameReplayClient; +import mage.interfaces.MageException; +import mage.interfaces.Server; +import mage.server.game.GameFactory; +import mage.server.game.GameManager; +import mage.server.game.GamesRoomManager; +import mage.server.game.PlayerFactory; +import mage.server.game.ReplayManager; +import mage.server.game.TableManager; +import mage.util.Logging; +import mage.view.TableView; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class ServerImpl implements Server { + + private final static Logger logger = Logging.getLogger("Mage Server"); + + public ServerImpl(int port, String name) { + try { + System.setSecurityManager(null); + Registry reg = LocateRegistry.createRegistry(port); + Server server = (Server) UnicastRemoteObject.exportObject(this, 0); + reg.rebind(name, server); + logger.info("Started MAGE server - listening on port " + port); + } catch (RemoteException ex) { + logger.log(Level.SEVERE, "Failed to start RMI server at port " + port, ex); + } + + } + + @Override + public UUID registerClient(Client c) throws RemoteException { + + UUID sessionId = SessionManager.getInstance().createSession(c); + logger.info("Session " + sessionId + " created for client " + c.getId()); + return sessionId; + + } + + @Override + public TableView createTable(UUID sessionId, UUID roomId, String gameType, DeckType deckType, List playerTypes) throws MageException { + try { + TableView table = GamesRoomManager.getInstance().getRoom(roomId).createTable(sessionId, gameType, deckType, playerTypes); + logger.info("Table " + table.getTableId() + " created"); + return table; + } + catch (Exception ex) { + handleException(ex); + } + return null; + } + + @Override + public void removeTable(UUID sessionId, UUID roomId, UUID tableId) throws MageException { + try { + GamesRoomManager.getInstance().getRoom(roomId).removeTable(sessionId, tableId); + } + catch (Exception ex) { + handleException(ex); + } + } + + @Override + public boolean joinTable(UUID sessionId, UUID roomId, UUID tableId, int seatNum, String name, DeckCardLists deckList) throws MageException { + try { + boolean ret = GamesRoomManager.getInstance().getRoom(roomId).joinTable(sessionId, tableId, seatNum, name, deckList); + logger.info("Session " + sessionId + " joined table " + tableId); + return ret; + } + catch (Exception ex) { + handleException(ex); + } + return false; + } + + @Override + public Collection getTables(UUID roomId) throws MageException { + try { + return GamesRoomManager.getInstance().getRoom(roomId).getTables(); + } + catch (Exception ex) { + handleException(ex); + } + return null; + } + + @Override + public TableView getTable(UUID roomId, UUID tableId) throws MageException { + try { + return GamesRoomManager.getInstance().getRoom(roomId).getTable(tableId); + } + catch (Exception ex) { + handleException(ex); + } + return null; + } + + @Override + public void deregisterClient(UUID sessionId) throws MageException { + try { + SessionManager.getInstance().getSession(sessionId).kill(); + logger.info("Client deregistered ..."); + } + catch (Exception ex) { + handleException(ex); + } + } + + @Override + public void startGame(UUID sessionId, UUID roomId, UUID tableId) throws MageException { + try { + TableManager.getInstance().startGame(sessionId, roomId, tableId); + } + catch (Exception ex) { + handleException(ex); + } + } + + @Override + public void sendChatMessage(UUID chatId, String userName, String message) throws MageException { + try { + ChatManager.getInstance().broadcast(chatId, userName, message); + } + catch (Exception ex) { + handleException(ex); + } + } + + @Override + public void joinChat(UUID chatId, ChatClient chatClient) throws MageException { + try { + ChatManager.getInstance().joinChat(chatId, chatClient); + } + catch (Exception ex) { + handleException(ex); + } + } + + @Override + public void leaveChat(UUID chatId, UUID clientId) throws MageException { + try { + ChatManager.getInstance().leaveChat(chatId, clientId); + } + catch (Exception ex) { + handleException(ex); + } + } + + @Override + public UUID getMainRoomId() throws MageException { + try { + return GamesRoomManager.getInstance().getMainRoomId(); + } + catch (Exception ex) { + handleException(ex); + } + return null; + } + + @Override + public UUID getRoomChatId(UUID roomId) throws MageException { + try { + return GamesRoomManager.getInstance().getRoom(roomId).getChatId(); + } + catch (Exception ex) { + handleException(ex); + } + return null; + } + + @Override + public boolean isTableOwner(UUID sessionId, UUID roomId, UUID tableId) throws MageException { + try { + return TableManager.getInstance().isTableOwner(tableId, sessionId); + } + catch (Exception ex) { + handleException(ex); + } + return false; + } + + @Override + public void leaveTable(UUID sessionId, UUID roomId, UUID tableId) throws MageException { + try { + GamesRoomManager.getInstance().getRoom(roomId).leaveTable(sessionId, tableId); + } + catch (Exception ex) { + handleException(ex); + } + } + + @Override + public UUID getTableChatId(UUID tableId) throws MageException { + try { + return TableManager.getInstance().getChatId(tableId); + } + catch (Exception ex) { + handleException(ex); + } + return null; + } + + @Override + public void joinGame(UUID gameId, UUID sessionId, GameClient gameClient) throws MageException { + try { + GameManager.getInstance().joinGame(gameId, sessionId, gameClient); + } + catch (Exception ex) { + handleException(ex); + } + } + + @Override + public UUID getGameChatId(UUID gameId) throws MageException { + try { + return GameManager.getInstance().getChatId(gameId); + } + catch (Exception ex) { + handleException(ex); + } + return null; + } + + @Override + public void sendPlayerUUID(UUID gameId, UUID sessionId, UUID data) throws MageException { + try { + GameManager.getInstance().sendPlayerUUID(gameId, sessionId, data); + } + catch (Exception ex) { + handleException(ex); + } + } + + @Override + public void sendPlayerString(UUID gameId, UUID sessionId, String data) throws MageException { + try { + GameManager.getInstance().sendPlayerString(gameId, sessionId, data); + } + catch (Exception ex) { + handleException(ex); + } + } + + @Override + public void sendPlayerBoolean(UUID gameId, UUID sessionId, Boolean data) throws MageException { + try { + GameManager.getInstance().sendPlayerBoolean(gameId, sessionId, data); + } + catch (Exception ex) { + handleException(ex); + } + } + + @Override + public void sendPlayerInteger(UUID gameId, UUID sessionId, Integer data) throws RemoteException, MageException { + try { + GameManager.getInstance().sendPlayerInteger(gameId, sessionId, data); + } + catch (Exception ex) { + handleException(ex); + } + } + + @Override + public void concedeGame(UUID gameId, UUID sessionId) throws MageException { + try { + GameManager.getInstance().concedeGame(gameId, sessionId); + } + catch (Exception ex) { + handleException(ex); + } + } + + @Override + public boolean watchTable(UUID sessionId, UUID roomId, UUID tableId) throws MageException { + try { + return GamesRoomManager.getInstance().getRoom(roomId).watchTable(sessionId, tableId); + } + catch (Exception ex) { + handleException(ex); + } + return false; + } + + @Override + public void watchGame(UUID gameId, UUID sessionId, GameClient gameClient) throws MageException { + try { + GameManager.getInstance().watchGame(gameId, sessionId, gameClient); + } + catch (Exception ex) { + handleException(ex); + } + } + + @Override + public void stopWatching(UUID gameId, UUID clientId) throws MageException { + try { + GameManager.getInstance().stopWatching(gameId, clientId); + } + catch (Exception ex) { + handleException(ex); + } + } + + @Override + public void replayGame(UUID gameId, UUID sessionId, GameReplayClient replayClient) throws MageException { + try { + ReplayManager.getInstance().startReplay(sessionId, replayClient); + } + catch (Exception ex) { + handleException(ex); + } + } + + @Override + public void stopReplay(UUID sessionId) throws MageException { + try { + ReplayManager.getInstance().stopReplay(sessionId); + } + catch (Exception ex) { + handleException(ex); + } + } + + @Override + public void nextPlay(UUID sessionId) throws MageException { + try { + ReplayManager.getInstance().nextPlay(sessionId); + } + catch (Exception ex) { + handleException(ex); + } + } + + @Override + public void previousPlay(UUID sessionId) throws MageException { + try { + ReplayManager.getInstance().previousPlay(sessionId); + } + catch (Exception ex) { + handleException(ex); + } + } + + @Override + public boolean replayTable(UUID sessionId, UUID roomId, UUID tableId) throws MageException { + try { + return TableManager.getInstance().replayTable(sessionId, tableId); + } + catch (Exception ex) { + handleException(ex); + } + return false; + } + + @Override + public String[] getGameTypes() throws MageException { + try { + return GameFactory.getInstance().getGameTypes().toArray(new String[0]); + } + catch (Exception ex) { + handleException(ex); + } + return null; + } + + @Override + public String[] getPlayerTypes() throws MageException { + try { + return PlayerFactory.getInstance().getPlayerTypes().toArray(new String[0]); + } + catch (Exception ex) { + handleException(ex); + } + return null; + } + + @Override + public void cheat(UUID gameId, UUID sessionId, DeckCardLists deckList) throws MageException { + try { +// GameManager.getInstance().cheat(gameId, sessionId, deckList); + } + catch (Exception ex) { + handleException(ex); + } + } + + public void handleException(Exception ex) throws MageException { + logger.log(Level.SEVERE, "", ex); + throw new MageException("Server error"); + } + +} diff --git a/Mage.Server/src/mage/server/Session.java b/Mage.Server/src/mage/server/Session.java new file mode 100644 index 00000000000..5fde382c3a8 --- /dev/null +++ b/Mage.Server/src/mage/server/Session.java @@ -0,0 +1,132 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server; + +import mage.server.util.ThreadExecutor; +import java.rmi.RemoteException; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.logging.Level; +import java.util.logging.Logger; +import mage.interfaces.Client; +import mage.server.game.GameManager; +import mage.server.game.TableManager; +import mage.util.Logging; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class Session { + + private static ExecutorService executor = ThreadExecutor.getInstance().getRMIExecutor(); + private final static Logger logger = Logging.getLogger(Session.class.getName()); + + private UUID sessionId; + private String username; + private Client client; + + public Session(Client client) { + sessionId = UUID.randomUUID(); + try { + username = client.getUserName(); + } catch (RemoteException ex) { + logger.log(Level.SEVERE, null, ex); + } + this.client = client; + } + + public UUID getId() { + return sessionId; + } + + public void kill() { + SessionManager.getInstance().removeSession(sessionId); + TableManager.getInstance().removeSession(sessionId); + GameManager.getInstance().removeSession(sessionId); + + } + + public void gameStarted(final UUID gameId, final UUID playerId) { + executor.submit( + new Runnable() { + @Override + public void run() { + try { + client.gameStarted(gameId, playerId); + logger.info("game started for player " + playerId); + } + catch (RemoteException ex) { + logger.log(Level.WARNING, ex.getMessage()); + kill(); + } + } + } + ); + } + + public void watchGame(final UUID gameId) { + executor.submit( + new Runnable() { + @Override + public void run() { + try { + client.watchGame(gameId); + } + catch (RemoteException ex) { + logger.log(Level.WARNING, ex.getMessage()); + kill(); + } + } + } + ); + } + + public void replayGame(final UUID gameId) { + executor.submit( + new Runnable() { + @Override + public void run() { + try { + client.replayGame(gameId); + } + catch (RemoteException ex) { + logger.log(Level.WARNING, ex.getMessage()); + kill(); + } + } + } + ); + } + + public String getUsername() { + return username; + } + +} diff --git a/Mage.Server/src/mage/server/SessionManager.java b/Mage.Server/src/mage/server/SessionManager.java new file mode 100644 index 00000000000..61255fe1d65 --- /dev/null +++ b/Mage.Server/src/mage/server/SessionManager.java @@ -0,0 +1,64 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server; + +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import mage.interfaces.Client; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class SessionManager { + + private final static SessionManager INSTANCE = new SessionManager(); + + public static SessionManager getInstance() { + return INSTANCE; + } + + private ConcurrentHashMap sessions = new ConcurrentHashMap(); + + public Session getSession(UUID sessionId) { + return sessions.get(sessionId); + } + + public UUID createSession(Client c) { + Session session = new Session(c); + sessions.put(session.getId(), session); + return session.getId(); + } + + public void removeSession(UUID sessionId) { + sessions.remove(sessionId); + } + + +} diff --git a/Mage.Server/src/mage/server/game/GameCallback.java b/Mage.Server/src/mage/server/game/GameCallback.java new file mode 100644 index 00000000000..49cccafbf1a --- /dev/null +++ b/Mage.Server/src/mage/server/game/GameCallback.java @@ -0,0 +1,39 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server.game; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public interface GameCallback { + + public void gameResult(String result); + +} diff --git a/Mage.Server/src/mage/server/game/GameController.java b/Mage.Server/src/mage/server/game/GameController.java new file mode 100644 index 00000000000..e36443085fd --- /dev/null +++ b/Mage.Server/src/mage/server/game/GameController.java @@ -0,0 +1,354 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server.game; + +import java.util.Collection; +import java.util.Map.Entry; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.logging.Level; +import java.util.logging.Logger; +import mage.abilities.ActivatedAbility; +import mage.cards.Card; +import mage.cards.Cards; +import mage.cards.decks.Deck; +import mage.cards.decks.DeckCardLists; +import mage.game.Game; +import mage.game.GameReplay; +import mage.game.events.TableEvent; +import mage.interfaces.GameClient; +import mage.server.ChatManager; +import mage.server.util.ThreadExecutor; +import mage.game.events.Listener; +import mage.game.events.PlayerQueryEvent; +import mage.human.HumanPlayer; +import mage.players.Player; +import mage.util.Logging; +import mage.view.AbilityPickerView; +import mage.view.CardsView; +import mage.view.GameView; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class GameController implements GameCallback { + + private static ExecutorService gameExecutor = ThreadExecutor.getInstance().getGameExecutor(); + private final static Logger logger = Logging.getLogger(GameController.class.getName()); + + private ConcurrentHashMap gameSessions = new ConcurrentHashMap(); + private ConcurrentHashMap watchers = new ConcurrentHashMap(); + private ConcurrentHashMap sessionPlayerMap; + private UUID gameSessionId; + private Game game; + private UUID chatId; + private UUID tableId; + private Future gameFuture; + + + public GameController(Game game, ConcurrentHashMap sessionPlayerMap, UUID tableId) { + gameSessionId = UUID.randomUUID(); + this.sessionPlayerMap = sessionPlayerMap; + chatId = ChatManager.getInstance().createChatSession(); + this.game = game; + this.tableId = tableId; + init(); + } + + private void init() { + game.addTableEventListener( + new Listener () { + @Override + public void event(TableEvent event) { + switch (event.getEventType()) { + case UPDATE: + updateGame(); + break; + case INFO: + ChatManager.getInstance().broadcast(chatId, "", event.getMessage()); + logger.info(game.getId() + " " + event.getMessage()); + break; + } + } + } + ); + game.addPlayerQueryEventListener( + new Listener () { + @Override + public void event(PlayerQueryEvent event) { + switch (event.getQueryType()) { + case ASK: + ask(event.getPlayerId(), event.getMessage()); + break; + case PICK_TARGET: + target(event.getPlayerId(), event.getMessage(), event.getCards(), event.isRequired()); + break; + case SELECT: + select(event.getPlayerId(), event.getMessage()); + break; + case PLAY_MANA: + playMana(event.getPlayerId(), event.getMessage()); + break; + case PLAY_X_MANA: + playXMana(event.getPlayerId(), event.getMessage()); + break; + case CHOOSE_ABILITY: + chooseAbility(event.getPlayerId(), event.getAbilities()); + break; + case CHOOSE: + choose(event.getPlayerId(), event.getMessage(), event.getChoices()); + break; + case AMOUNT: + amount(event.getPlayerId(), event.getMessage(), event.getMin(), event.getMax()); + break; + } + } + } + ); + } + + private UUID getPlayerId(UUID sessionId) { + return sessionPlayerMap.get(sessionId); + } + + public void join(UUID sessionId, GameClient client) { + UUID playerId = sessionPlayerMap.get(sessionId); + GameSession gameSession = new GameSession(client, game, sessionId, playerId); + gameSessions.put(playerId, gameSession); + logger.log(Level.INFO, "player " + playerId + " has joined game " + game.getId()); + gameSession.init(getGameView(playerId)); + ChatManager.getInstance().broadcast(chatId, "", game.getPlayer(playerId).getName() + " has joined the game"); + if (allJoined()) { + startGame(); + } + } + + private synchronized void startGame() { + if (gameFuture == null) { + GameWorker worker = new GameWorker(game, this); + gameFuture = gameExecutor.submit(worker); + } + } + + private boolean allJoined() { + for (Player player: game.getPlayers().values()) { + if (player instanceof HumanPlayer && gameSessions.get(player.getId()) == null) { + return false; + } + } + return true; + } + + public void watch(UUID sessionId, GameClient client) { + GameWatcher gameWatcher = new GameWatcher(client, sessionId, game.getId()); + watchers.put(sessionId, gameWatcher); + gameWatcher.init(getGameView()); + ChatManager.getInstance().broadcast(chatId, "", " has started watching"); + } + + public GameReplay createReplay() { + if (game.isGameOver()) { + return new GameReplay(game.getGameStates()); + } + return null; + } + + public void stopWatching(UUID sessionId) { + watchers.remove(sessionId); + ChatManager.getInstance().broadcast(chatId, "", " has stopped watching"); + } + + public void concede(UUID sessionId) { + game.concede(getPlayerId(sessionId)); + } + + private void leave(UUID sessionId) { + game.quit(getPlayerId(sessionId)); + } + + public void cheat(UUID sessionId, DeckCardLists deckList) { + Player player = game.getPlayer(getPlayerId(sessionId)); + Deck deck = Deck.load(deckList); + deck.setOwnerId(player.getId()); + for (Card card: deck.getCards().values()) { + player.putOntoBattlefield(card, game); + } + updateGame(); + } + +// public void timeout(UUID sessionId) { +// kill(sessionId); +// } + + public void kill(UUID sessionId) { + if (sessionPlayerMap.containsKey(sessionId)) { + gameSessions.get(sessionPlayerMap.get(sessionId)).setKilled(); + gameSessions.remove(sessionPlayerMap.get(sessionId)); + leave(sessionId); + sessionPlayerMap.remove(sessionId); + } + if (watchers.containsKey(sessionId)) { + watchers.get(sessionId).setKilled(); + watchers.remove(sessionId); + } + } + + public void timeout(UUID sessionId) { + if (sessionPlayerMap.containsKey(sessionId)) { + ChatManager.getInstance().broadcast(chatId, "", game.getPlayer(sessionPlayerMap.get(sessionId)).getName() + " has timed out. Auto concede."); + concede(sessionId); + } + } + + public void endGame(final String message) { + for (final GameSession gameSession: gameSessions.values()) { + gameSession.gameOver(message); + } + for (final GameWatcher gameWatcher: watchers.values()) { + gameWatcher.gameOver(message); + } + TableManager.getInstance().endGame(tableId); + } + + public UUID getSessionId() { + return this.gameSessionId; + } + + public UUID getChatId() { + return chatId; + } + + public void sendPlayerUUID(UUID sessionId, UUID data) { + gameSessions.get(sessionPlayerMap.get(sessionId)).sendPlayerUUID(data); + } + + public void sendPlayerString(UUID sessionId, String data) { + gameSessions.get(sessionPlayerMap.get(sessionId)).sendPlayerString(data); + } + + public void sendPlayerBoolean(UUID sessionId, Boolean data) { + gameSessions.get(sessionPlayerMap.get(sessionId)).sendPlayerBoolean(data); + } + + public void sendPlayerInteger(UUID sessionId, Integer data) { + gameSessions.get(sessionPlayerMap.get(sessionId)).sendPlayerInteger(data); + } + + private void updateGame() { + + for (final Entry entry: gameSessions.entrySet()) { + entry.getValue().update(getGameView(entry.getKey())); + } + for (final GameWatcher gameWatcher: watchers.values()) { + gameWatcher.update(getGameView()); + } + } + + private void ask(UUID playerId, String question) { + informOthers(playerId); + gameSessions.get(playerId).ask(question, getGameView(playerId)); + } + + private void chooseAbility(UUID playerId, Collection choices) { + informOthers(playerId); + gameSessions.get(playerId).chooseAbility(new AbilityPickerView(choices)); + } + + private void choose(UUID playerId, String message, String[] choices) { + informOthers(playerId); + gameSessions.get(playerId).choose(message, choices); + } + + private void target(UUID playerId, String question, Cards cards, boolean required) { + informOthers(playerId); + gameSessions.get(playerId).target(question, getCardView(cards), required, getGameView(playerId)); + } + + private void select(UUID playerId, String message) { + informOthers(playerId); + gameSessions.get(playerId).select(message, getGameView(playerId)); + } + + private void playMana(UUID playerId, String message) { + informOthers(playerId); + gameSessions.get(playerId).playMana(message, getGameView(playerId)); + } + + private void playXMana(UUID playerId, String message) { + informOthers(playerId); + gameSessions.get(playerId).playXMana(message, getGameView(playerId)); + } + + private void amount(UUID playerId, String message, int min, int max) { + informOthers(playerId); + gameSessions.get(playerId).getAmount(message, min, max); + } + + private void revealCards(String name, Cards cards) { + for (GameSession session: gameSessions.values()) { + session.revealCards(name, getCardView(cards)); + } + } + + private void informOthers(UUID playerId) { + final String message = "Waiting for " + game.getPlayer(playerId).getName(); + for (final Entry entry: gameSessions.entrySet()) { + if (!entry.getKey().equals(playerId)) { + entry.getValue().inform(message, getGameView(entry.getKey())); + } + } + for (final GameWatcher watcher: watchers.values()) { + watcher.inform(message, getGameView()); + } + } + + private GameView getGameView() { + return new GameView(game.getState()); + } + + private GameView getGameView(UUID playerId) { + GameView gameView = new GameView(game.getState()); + gameView.setHand(getCardView(game.getPlayer(playerId).getHand())); + return gameView; + } + + private CardsView getCardView(Cards cards) { + if (cards == null) + return null; + return new CardsView(cards.values()); + } + + public void gameResult(String result) { + endGame(result); + } + +} diff --git a/Mage.Server/src/mage/server/game/GameFactory.java b/Mage.Server/src/mage/server/game/GameFactory.java new file mode 100644 index 00000000000..cbf0a0c8e8f --- /dev/null +++ b/Mage.Server/src/mage/server/game/GameFactory.java @@ -0,0 +1,82 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server.game; + +import java.lang.reflect.Constructor; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import mage.game.Game; +import mage.util.Logging; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class GameFactory { + + private final static GameFactory INSTANCE = new GameFactory(); + private final static Logger logger = Logging.getLogger(GameFactory.class.getName()); + + private Map gameTypes = new HashMap(); + + public static GameFactory getInstance() { + return INSTANCE; + } + + private GameFactory() {} + + public Game createGame(String gameType) { + + Game game; + Constructor con; + try { + con = gameTypes.get(gameType).getConstructor(new Class[]{}); + game = (Game)con.newInstance(new Object[] {}); + } catch (Exception ex) { + logger.log(Level.SEVERE, null, ex); + return null; + } + logger.info("Game created: " + game.getId().toString()); + + return game; + + } + + public Set getGameTypes() { + return gameTypes.keySet(); + } + + public void addGameType(String name, Class gameType) { + this.gameTypes.put(name, gameType); + } + +} diff --git a/Mage.Server/src/mage/server/game/GameManager.java b/Mage.Server/src/mage/server/game/GameManager.java new file mode 100644 index 00000000000..9ea25bcafa7 --- /dev/null +++ b/Mage.Server/src/mage/server/game/GameManager.java @@ -0,0 +1,125 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server.game; + +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import mage.cards.decks.DeckCardLists; +import mage.game.Game; +import mage.game.GameReplay; +import mage.interfaces.GameClient; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class GameManager { + private final static GameManager INSTANCE = new GameManager(); + + public static GameManager getInstance() { + return INSTANCE; + } + + private GameManager() {} + + private ConcurrentHashMap gameControllers = new ConcurrentHashMap(); + + public UUID createGameSession(Game game, ConcurrentHashMap sessionPlayerMap, UUID tableId) { + GameController gameController = new GameController(game, sessionPlayerMap, tableId); + gameControllers.put(game.getId(), gameController); + return gameController.getSessionId(); + } + + public void joinGame(UUID gameId, UUID sessionId, GameClient client) { + gameControllers.get(gameId).join(sessionId, client); + } + +// public void leaveGame(UUID gameId, UUID clientId) { +// gameControllers.get(gameId).leave(clientId); +// } + + public void destroyChatSession(UUID gameId) { + gameControllers.remove(gameId); + } + + public UUID getChatId(UUID gameId) { + return gameControllers.get(gameId).getChatId(); + } + + public void sendPlayerUUID(UUID gameId, UUID sessionId, UUID data) { + gameControllers.get(gameId).sendPlayerUUID(sessionId, data); + } + + public void sendPlayerString(UUID gameId, UUID sessionId, String data) { + gameControllers.get(gameId).sendPlayerString(sessionId, data); + } + + public void sendPlayerBoolean(UUID gameId, UUID sessionId, Boolean data) { + gameControllers.get(gameId).sendPlayerBoolean(sessionId, data); + } + + public void sendPlayerInteger(UUID gameId, UUID sessionId, Integer data) { + gameControllers.get(gameId).sendPlayerInteger(sessionId, data); + } + + public void concedeGame(UUID gameId, UUID sessionId) { + gameControllers.get(gameId).concede(sessionId); + } + + public void watchGame(UUID gameId, UUID sessionId, GameClient client) { + gameControllers.get(gameId).watch(sessionId, client); + } + + public void stopWatching(UUID gameId, UUID clientId) { + gameControllers.get(gameId).stopWatching(clientId); + } + + public void removeSession(UUID sessionId) { + for (GameController controller: gameControllers.values()) { + controller.kill(sessionId); + } + } + + public void kill(UUID gameId, UUID sessionId) { + gameControllers.get(gameId).kill(sessionId); + } + + public GameReplay createReplay(UUID gameId) { + return gameControllers.get(gameId).createReplay(); + } + + public void cheat(UUID gameId, UUID sessionId, DeckCardLists deckList) { + gameControllers.get(gameId).cheat(sessionId, deckList); + } + + void timeout(UUID gameId, UUID sessionId) { + gameControllers.get(gameId).timeout(sessionId); + } + +} diff --git a/Mage.Server/src/mage/server/game/GameSession.java b/Mage.Server/src/mage/server/game/GameSession.java new file mode 100644 index 00000000000..3ccea1b32af --- /dev/null +++ b/Mage.Server/src/mage/server/game/GameSession.java @@ -0,0 +1,262 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server.game; + +import java.rmi.RemoteException; +import java.util.UUID; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import mage.game.Game; +import mage.interfaces.GameClient; +import mage.server.util.ConfigSettings; +import mage.server.util.ThreadExecutor; +import mage.view.AbilityPickerView; +import mage.view.CardsView; +import mage.view.GameView; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class GameSession extends GameWatcher { + + private Game game; + private UUID playerId; + + private ScheduledFuture futureTimeout; + protected static ScheduledExecutorService timeoutExecutor = ThreadExecutor.getInstance().getTimeoutExecutor(); + + public GameSession(GameClient client, Game game, UUID sessionId, UUID playerId) { + super(client, sessionId, game.getId()); + this.game = game; + this.playerId = playerId; + } + + public void ask(final String question, final GameView gameView) { + if (!killed) { + setupTimeout(); + rmiExecutor.submit( + new Runnable() { + @Override + public void run() { + try { + client.ask(question, gameView); + } catch (RemoteException ex) { + handleRemoteException(ex); + } + } + } + ); + } + } + + public void target(final String question, final CardsView cardView, final boolean required, final GameView gameView) { + if (!killed) { + setupTimeout(); + rmiExecutor.submit( + new Runnable() { + @Override + public void run() { + try { + client.target(question, cardView, required, gameView); + } catch (RemoteException ex) { + handleRemoteException(ex); + } + } + } + ); + } + } + + public void select(final String message, final GameView gameView) { + if (!killed) { + setupTimeout(); + rmiExecutor.submit( + new Runnable() { + @Override + public void run() { + try { + client.select(message, gameView); + } catch (RemoteException ex) { + handleRemoteException(ex); + } + } + } + ); + } + } + + public void chooseAbility(final AbilityPickerView abilities) { + if (!killed) { + setupTimeout(); + rmiExecutor.submit( + new Runnable() { + @Override + public void run() { + try { + client.chooseAbility(abilities); + } catch (RemoteException ex) { + handleRemoteException(ex); + } + } + } + ); + } + } + + public void choose(final String message, final String[] choices) { + if (!killed) { + setupTimeout(); + rmiExecutor.submit( + new Runnable() { + @Override + public void run() { + try { + client.choose(message, choices); + } catch (RemoteException ex) { + handleRemoteException(ex); + } + } + } + ); + } + } + + public void playMana(final String message, final GameView gameView) { + if (!killed) { + setupTimeout(); + rmiExecutor.submit( + new Runnable() { + @Override + public void run() { + try { + client.playMana(message, gameView); + } catch (RemoteException ex) { + handleRemoteException(ex); + } + } + } + ); + } + } + + public void playXMana(final String message, final GameView gameView) { + if (!killed) { + setupTimeout(); + rmiExecutor.submit( + new Runnable() { + @Override + public void run() { + try { + client.playXMana(message, gameView); + } catch (RemoteException ex) { + handleRemoteException(ex); + } + } + } + ); + } + } + + public void getAmount(final String message, final int min, final int max) { + if (!killed) { + setupTimeout(); + rmiExecutor.submit( + new Runnable() { + @Override + public void run() { + try { + client.getAmount(min, max); + } catch (RemoteException ex) { + handleRemoteException(ex); + } + } + } + ); + } + } + + public void revealCards(final String name, final CardsView cardView) { + if (!killed) { + rmiExecutor.submit( + new Runnable() { + @Override + public void run() { + try { + client.revealCards(name, cardView); + } catch (RemoteException ex) { + handleRemoteException(ex); + } + } + } + ); + } + } + + + private synchronized void setupTimeout() { + cancelTimeout(); + futureTimeout = timeoutExecutor.schedule( + new Runnable() { + @Override + public void run() { + GameManager.getInstance().timeout(gameId, sessionId); + } + }, + ConfigSettings.getInstance().getMaxSecondsIdle(), TimeUnit.SECONDS + ); + } + + private synchronized void cancelTimeout() { + if (futureTimeout != null) { + futureTimeout.cancel(false); + } + } + + public void sendPlayerUUID(UUID data) { + cancelTimeout(); + game.getPlayer(playerId).setResponseUUID(data); + } + + public void sendPlayerString(String data) { + cancelTimeout(); + game.getPlayer(playerId).setResponseString(data); + } + + public void sendPlayerBoolean(Boolean data) { + cancelTimeout(); + game.getPlayer(playerId).setResponseBoolean(data); + } + + public void sendPlayerInteger(Integer data) { + cancelTimeout(); + game.getPlayer(playerId).setResponseInteger(data); + } +} diff --git a/Mage.Server/src/mage/server/game/GameWatcher.java b/Mage.Server/src/mage/server/game/GameWatcher.java new file mode 100644 index 00000000000..dcb9d666f9e --- /dev/null +++ b/Mage.Server/src/mage/server/game/GameWatcher.java @@ -0,0 +1,135 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server.game; + +import java.rmi.RemoteException; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.logging.Level; +import java.util.logging.Logger; +import mage.interfaces.GameClient; +import mage.server.util.ThreadExecutor; +import mage.util.Logging; +import mage.view.GameView; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class GameWatcher { + + protected static ExecutorService rmiExecutor = ThreadExecutor.getInstance().getRMIExecutor(); + protected final static Logger logger = Logging.getLogger(GameWatcher.class.getName()); + + protected GameClient client; + + protected UUID sessionId; + protected UUID gameId; + protected boolean killed = false; + + public GameWatcher(GameClient client, UUID sessionId, UUID gameId) { + this.client = client; + this.sessionId = sessionId; + this.gameId = gameId; + } + + public void init(final GameView gameView) { + if (!killed) + rmiExecutor.submit( + new Runnable() { + @Override + public void run() { + try { + client.init(gameView); + } catch (RemoteException ex) { + handleRemoteException(ex); + } + } + } + ); + } + + public void update(final GameView gameView) { + if (!killed) + rmiExecutor.submit( + new Runnable() { + @Override + public void run() { + try { + client.update(gameView); + } catch (RemoteException ex) { + handleRemoteException(ex); + } + } + } + ); + } + + public void inform(final String message, final GameView gameView) { + if (!killed) + rmiExecutor.submit( + new Runnable() { + @Override + public void run() { + try { + client.inform(message, gameView); + } catch (RemoteException ex) { + handleRemoteException(ex); + } + } + } + ); + } + + public void gameOver(final String message) { + if (!killed) + rmiExecutor.submit( + new Runnable() { + @Override + public void run() { + try { + client.gameOver(message); + } catch (RemoteException ex) { + handleRemoteException(ex); + } + } + } + ); + } + + protected void handleRemoteException(RemoteException ex) { + logger.log(Level.SEVERE, null, ex); + GameManager.getInstance().kill(gameId, sessionId); + } + + public void setKilled() { + killed = true; + } + +} diff --git a/Mage.Server/src/mage/server/game/GameWorker.java b/Mage.Server/src/mage/server/game/GameWorker.java new file mode 100644 index 00000000000..124df4e996d --- /dev/null +++ b/Mage.Server/src/mage/server/game/GameWorker.java @@ -0,0 +1,64 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server.game; + +import java.util.concurrent.Callable; +import java.util.logging.Level; +import java.util.logging.Logger; +import mage.game.Game; +import mage.util.Logging; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class GameWorker implements Callable { + + private final static Logger logger = Logging.getLogger(GameWorker.class.getName()); + + private GameCallback result; + private Game game; + + public GameWorker(Game game, GameCallback result) { + this.game = game; + this.result = result; + } + + @Override + public Object call() { + try { + game.start(); + result.gameResult(game.getWinner()); + } catch (Exception ex) { + logger.log(Level.SEVERE, null, ex); + } + return null; + } + +} diff --git a/Mage.Server/src/mage/server/game/GamesRoom.java b/Mage.Server/src/mage/server/game/GamesRoom.java new file mode 100644 index 00000000000..2a681be20ff --- /dev/null +++ b/Mage.Server/src/mage/server/game/GamesRoom.java @@ -0,0 +1,53 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server.game; + +import java.util.Collection; +import java.util.List; +import java.util.UUID; +import mage.Constants.DeckType; +import mage.cards.decks.DeckCardLists; +import mage.view.TableView; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public interface GamesRoom extends Room { + + public Collection getTables(); + public boolean joinTable(UUID sessionId, UUID tableId, int seatNum, String name, DeckCardLists deckList); + public TableView createTable(UUID sessionId, String gameType, DeckType deckType, List playerTypes); + public void removeTable(UUID sessionId, UUID tableId); + public TableView getTable(UUID tableId); + public void leaveTable(UUID sessionId, UUID tableId); + + public boolean watchTable(UUID sessionId, UUID tableId); + +} diff --git a/Mage.Server/src/mage/server/game/GamesRoomImpl.java b/Mage.Server/src/mage/server/game/GamesRoomImpl.java new file mode 100644 index 00000000000..6e9caac3533 --- /dev/null +++ b/Mage.Server/src/mage/server/game/GamesRoomImpl.java @@ -0,0 +1,101 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server.game; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; +import mage.Constants.DeckType; +import mage.cards.decks.DeckCardLists; +import mage.game.Table; +import mage.util.Logging; +import mage.view.TableView; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable { + + private final static Logger logger = Logging.getLogger(GamesRoomImpl.class.getName()); + + private ConcurrentHashMap tables = new ConcurrentHashMap(); + + @Override + public Collection getTables() { + ArrayList tableList = new ArrayList(); + for (Table table: tables.values()) { + tableList.add(new TableView(table)); + } + return tableList; + } + + @Override + public boolean joinTable(UUID sessionId, UUID tableId, int seatNum, String name, DeckCardLists deckList) { + if (tables.containsKey(tableId)) { + return TableManager.getInstance().joinTable(sessionId, tableId, seatNum, name, deckList); + } else { + return false; + } + } + + @Override + public TableView createTable(UUID sessionId, String gameType, DeckType deckType, List playerTypes) { + Table table = TableManager.getInstance().createTable(sessionId, gameType, deckType, playerTypes); + tables.put(table.getId(), table); + return new TableView(table); + } + + @Override + public TableView getTable(UUID tableId) { + return new TableView(tables.get(tableId)); + } + + @Override + public void removeTable(UUID sessionId, UUID tableId) { + if (TableManager.getInstance().removeTable(sessionId, tableId)) { + tables.remove(tableId); + } + } + + @Override + public void leaveTable(UUID sessionId, UUID tableId) { + TableManager.getInstance().leaveTable(sessionId, tableId); + } + + @Override + public boolean watchTable(UUID sessionId, UUID tableId) { + return TableManager.getInstance().watchTable(sessionId, tableId); + } + +} diff --git a/Mage.Server/src/mage/server/game/GamesRoomManager.java b/Mage.Server/src/mage/server/game/GamesRoomManager.java new file mode 100644 index 00000000000..c0cfd0278d0 --- /dev/null +++ b/Mage.Server/src/mage/server/game/GamesRoomManager.java @@ -0,0 +1,72 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server.game; + +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; +import mage.util.Logging; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class GamesRoomManager { + + private final static GamesRoomManager INSTANCE = new GamesRoomManager(); + private final static Logger logger = Logging.getLogger(GamesRoomManager.class.getName()); + + private ConcurrentHashMap rooms = new ConcurrentHashMap(); + private UUID mainRoomId; + + public static GamesRoomManager getInstance() { + return INSTANCE; + } + + private GamesRoomManager() { + GamesRoom mainRoom = new GamesRoomImpl(); + mainRoomId = mainRoom.getRoomId(); + rooms.put(mainRoomId, mainRoom); + } + + public UUID createRoom() { + GamesRoom room = new GamesRoomImpl(); + rooms.put(room.getRoomId(), room); + return room.getRoomId(); + } + + public UUID getMainRoomId() { + return mainRoomId; + } + + public GamesRoom getRoom(UUID roomId) { + return rooms.get(roomId); + } + +} diff --git a/Mage.Server/src/mage/server/game/PlayerFactory.java b/Mage.Server/src/mage/server/game/PlayerFactory.java new file mode 100644 index 00000000000..81914a83700 --- /dev/null +++ b/Mage.Server/src/mage/server/game/PlayerFactory.java @@ -0,0 +1,79 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server.game; + +import java.lang.reflect.Constructor; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import mage.players.Player; +import mage.util.Logging; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class PlayerFactory { + + private final static PlayerFactory INSTANCE = new PlayerFactory(); + private final static Logger logger = Logging.getLogger(PlayerFactory.class.getName()); + + private Map playerTypes = new HashMap(); + + public static PlayerFactory getInstance() { + return INSTANCE; + } + + private PlayerFactory() {} + + public Player createPlayer(String playerType, String name) { + Player player; + Constructor con; + try { + con = playerTypes.get(playerType).getConstructor(new Class[]{String.class}); + player = (Player)con.newInstance(new Object[] {name}); + } catch (Exception ex) { + logger.log(Level.SEVERE, null, ex); + return null; + } + logger.info("Player created: " + player.getId().toString()); + return player; + } + + public Set getPlayerTypes() { + return playerTypes.keySet(); + } + + public void addPlayerType(String name, Class gameType) { + this.playerTypes.put(name, gameType); + } + +} diff --git a/Mage.Server/src/mage/server/game/ReplayManager.java b/Mage.Server/src/mage/server/game/ReplayManager.java new file mode 100644 index 00000000000..c341be00a09 --- /dev/null +++ b/Mage.Server/src/mage/server/game/ReplayManager.java @@ -0,0 +1,73 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server.game; + +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import mage.interfaces.GameReplayClient; +import mage.server.SessionManager; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class ReplayManager { + private final static ReplayManager INSTANCE = new ReplayManager(); + + public static ReplayManager getInstance() { + return INSTANCE; + } + + private ReplayManager() {} + + private ConcurrentHashMap replaySessions = new ConcurrentHashMap(); + + public void replayGame(UUID sessionId, UUID gameId) { + ReplaySession replaySession = new ReplaySession(gameId); + replaySessions.put(sessionId, replaySession); + SessionManager.getInstance().getSession(sessionId).replayGame(gameId); + } + + public void startReplay(UUID sessionId, GameReplayClient replayClient) { + replaySessions.get(sessionId).replay(replayClient); + } + + public void stopReplay(UUID sessionId) { + replaySessions.get(sessionId).stop(); + } + + public void nextPlay(UUID sessionId) { + replaySessions.get(sessionId).next(); + } + + public void previousPlay(UUID sessionId) { + replaySessions.get(sessionId).previous(); + } + +} diff --git a/Mage.Server/src/mage/server/game/ReplaySession.java b/Mage.Server/src/mage/server/game/ReplaySession.java new file mode 100644 index 00000000000..11583cea9cc --- /dev/null +++ b/Mage.Server/src/mage/server/game/ReplaySession.java @@ -0,0 +1,115 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server.game; + +import java.rmi.RemoteException; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.logging.Level; +import java.util.logging.Logger; +import mage.game.GameReplay; +import mage.game.GameState; +import mage.interfaces.GameReplayClient; +import mage.server.util.ThreadExecutor; +import mage.util.Logging; +import mage.view.GameView; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class ReplaySession implements GameCallback { + + protected static ExecutorService rmiExecutor = ThreadExecutor.getInstance().getRMIExecutor(); + private final static Logger logger = Logging.getLogger(ReplaySession.class.getName()); + + private GameReplay game; + private GameReplayClient client; + + ReplaySession(UUID gameId) { + this.game = GameManager.getInstance().createReplay(gameId); + } + + public void replay(GameReplayClient replayClient) { + this.client = replayClient; + game.start(); + rmiExecutor.submit( + new Runnable() { + public void run() { + try { + client.init(new GameView(game.next())); + } catch (RemoteException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + } + ); + } + + public void stop() { + gameResult("stopped replay"); + } + + public synchronized void next() { + updateGame(game.next()); + } + + public synchronized void previous() { + updateGame(game.previous()); + } + + public void gameResult(final String result) { + rmiExecutor.submit( + new Runnable() { + public void run() { + try { + client.gameOver(result); + } catch (RemoteException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + } + ); + } + + private void updateGame(final GameState state) { + rmiExecutor.submit( + new Runnable() { + public void run() { + try { + client.update(new GameView(state)); + } catch (RemoteException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + } + ); + } + +} diff --git a/Mage.Server/src/mage/server/game/Room.java b/Mage.Server/src/mage/server/game/Room.java new file mode 100644 index 00000000000..61b42108721 --- /dev/null +++ b/Mage.Server/src/mage/server/game/Room.java @@ -0,0 +1,41 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server.game; + +import java.rmi.Remote; +import java.util.UUID; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public interface Room extends Remote { + public UUID getChatId(); + public UUID getRoomId(); +} diff --git a/Mage.Server/src/mage/server/game/RoomImpl.java b/Mage.Server/src/mage/server/game/RoomImpl.java new file mode 100644 index 00000000000..f1391caa428 --- /dev/null +++ b/Mage.Server/src/mage/server/game/RoomImpl.java @@ -0,0 +1,66 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server.game; + +import java.util.UUID; +import mage.server.ChatManager; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public abstract class RoomImpl implements Room { + + private UUID chatId; + private UUID roomId; + + public RoomImpl() { + roomId = UUID.randomUUID(); + chatId = ChatManager.getInstance().createChatSession(); + } + + /** + * @return the chatId + */ + @Override + public UUID getChatId() { + return chatId; + } + + /** + * @return the roomId + */ + @Override + public UUID getRoomId() { + return roomId; + } + + + +} diff --git a/Mage.Server/src/mage/server/game/TableController.java b/Mage.Server/src/mage/server/game/TableController.java new file mode 100644 index 00000000000..066013b1752 --- /dev/null +++ b/Mage.Server/src/mage/server/game/TableController.java @@ -0,0 +1,156 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server.game; + +import java.util.List; +import java.util.Map.Entry; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import mage.Constants.DeckType; +import mage.Constants.TableState; +import mage.cards.decks.Deck; +import mage.cards.decks.DeckCardLists; +import mage.game.Game; +import mage.game.GameException; +import mage.game.Seat; +import mage.game.Table; +import mage.players.Player; +import mage.server.ChatManager; +import mage.server.SessionManager; +import mage.util.Logging; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class TableController { + + private final static Logger logger = Logging.getLogger(TableController.class.getName()); + + private UUID sessionId; + private UUID chatId; + private Table table; + private Game game; + private ConcurrentHashMap sessionPlayerMap = new ConcurrentHashMap(); + + public TableController(UUID sessionId, String gameType, DeckType deckType, List playerTypes) { + this.sessionId = sessionId; + chatId = ChatManager.getInstance().createChatSession(); + game = GameFactory.getInstance().createGame(gameType); + table = new Table(game, deckType, playerTypes); + } + + public synchronized boolean joinTable(UUID sessionId, int seatNum, String name, DeckCardLists deckList) { + if (table.getState() != TableState.WAITING) { + return false; + } + try { + Seat seat = table.getSeats()[seatNum]; + Deck deck = Deck.load(deckList); + if (validDeck(deck)) { + Player player = createPlayer(name, deck, seat.getPlayerType()); + table.joinTable(player, seatNum); + logger.info("player joined " + player.getId()); + //only add human players to sessionPlayerMap + if (table.getSeats()[seatNum].getPlayerType().equals("Human")) { + //TODO: add isHuman property to Player and check that instead + sessionPlayerMap.put(sessionId, player.getId()); + } + } + else { + throw new GameException("Invalid deck type"); + } + + } catch (GameException ex) { + logger.log(Level.WARNING, ex.getMessage()); + return false; + } + return true; + } + + public boolean watchTable(UUID sessionId) { + SessionManager.getInstance().getSession(sessionId).watchGame(game.getId()); + return true; + } + + public boolean replayTable(UUID sessionId) { + ReplayManager.getInstance().replayGame(sessionId, game.getId()); + return true; + } + + private boolean validDeck(Deck deck) { + return table.getValidator().validate(deck); + } + + private Player createPlayer(String name, Deck deck, String playerType) { + Player player = PlayerFactory.getInstance().createPlayer(playerType, name); + logger.info("Player created " + player.getId()); + player.setDeck(deck); + return player; + } + + public synchronized void leaveTable(UUID sessionId) { + if (table.getState() == TableState.WAITING) + table.leaveTable(sessionPlayerMap.get(sessionId)); + } + + public synchronized void startGame(UUID sessionId) { + if (sessionId.equals(this.sessionId) && table.getState() == TableState.STARTING) { + try { + table.initGame(); + } catch (GameException ex) { + logger.log(Level.SEVERE, null, ex); + } + GameManager.getInstance().createGameSession(game, sessionPlayerMap, table.getId()); + SessionManager sessionManager = SessionManager.getInstance(); + for (Entry entry: sessionPlayerMap.entrySet()) { + sessionManager.getSession(entry.getKey()).gameStarted(game.getId(), entry.getValue()); + } + } + } + + public void endGame() { + table.endGame(); + } + + public boolean isOwner(UUID sessionId) { + return sessionId.equals(this.sessionId); + } + + public Table getTable() { + return table; + } + + public UUID getChatId() { + return chatId; + } + +} diff --git a/Mage.Server/src/mage/server/game/TableManager.java b/Mage.Server/src/mage/server/game/TableManager.java new file mode 100644 index 00000000000..a1333ea185e --- /dev/null +++ b/Mage.Server/src/mage/server/game/TableManager.java @@ -0,0 +1,116 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server.game; + +import java.util.Collection; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; +import mage.Constants.DeckType; +import mage.cards.decks.DeckCardLists; +import mage.game.Table; +import mage.util.Logging; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class TableManager { + + private final static TableManager INSTANCE = new TableManager(); + private final static Logger logger = Logging.getLogger(TableManager.class.getName()); + + private ConcurrentHashMap controllers = new ConcurrentHashMap(); + private ConcurrentHashMap tables = new ConcurrentHashMap(); + + public static TableManager getInstance() { + return INSTANCE; + } + + public Table createTable(UUID sessionId, String gameType, DeckType deckType, List playerTypes) { + TableController tableController = new TableController(sessionId, gameType, deckType, playerTypes); + controllers.put(tableController.getTable().getId(), tableController); + tables.put(tableController.getTable().getId(), tableController.getTable()); + return tableController.getTable(); + } + + public Table getTable(UUID tableId) { + return tables.get(tableId); + } + + public Collection getTables() { + return tables.values(); + } + + public boolean joinTable(UUID sessionId, UUID tableId, int seatNum, String name, DeckCardLists deckList) { + return controllers.get(tableId).joinTable(sessionId, seatNum, name, deckList); + } + + public void removeSession(UUID sessionId) { + // TODO: search through tables and remove session + } + + public boolean isTableOwner(UUID tableId, UUID sessionId) { + return controllers.get(tableId).isOwner(sessionId); + } + + public boolean removeTable(UUID sessionId, UUID tableId) { + if (isTableOwner(tableId, sessionId)) { + controllers.remove(tableId); + tables.remove(tableId); + return true; + } + return false; + } + + public void leaveTable(UUID sessionId, UUID tableId) { + controllers.get(tableId).leaveTable(sessionId); + } + + public UUID getChatId(UUID tableId) { + return controllers.get(tableId).getChatId(); + } + + public void startGame(UUID sessionId, UUID roomId, UUID tableId) { + controllers.get(tableId).startGame(sessionId); + } + + public boolean watchTable(UUID sessionId, UUID tableId) { + return controllers.get(tableId).watchTable(sessionId); + } + + public boolean replayTable(UUID sessionId, UUID tableId) { + return controllers.get(tableId).replayTable(sessionId); + } + + public void endGame(UUID tableId) { + controllers.get(tableId).endGame(); + } +} diff --git a/Mage.Server/src/mage/server/util/Config.java b/Mage.Server/src/mage/server/util/Config.java new file mode 100644 index 00000000000..ff020a26354 --- /dev/null +++ b/Mage.Server/src/mage/server/util/Config.java @@ -0,0 +1,63 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server.util; + +import java.io.IOException; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; +import mage.util.Logging; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class Config { + + private final static Logger logger = Logging.getLogger(Config.class.getName()); + + static { + Properties p = new Properties(); + try { + p.load(Config.class.getResourceAsStream("resources/config.properties")); + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + port = Integer.parseInt(p.getProperty("port")); + remoteServer = p.getProperty("remote-server"); + maxGameThreads = Integer.parseInt(p.getProperty("max-game-threads")); + maxSecondsIdle = Integer.parseInt(p.getProperty("max-seconds-idle")); + } + + public static final String remoteServer; + public static final int port; + public static final int maxGameThreads; + public static final int maxSecondsIdle; + +} diff --git a/Mage.Server/src/mage/server/util/Config.xsd b/Mage.Server/src/mage/server/util/Config.xsd new file mode 100644 index 00000000000..8a7a7c67fa0 --- /dev/null +++ b/Mage.Server/src/mage/server/util/Config.xsd @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mage.Server/src/mage/server/util/ConfigSettings.java b/Mage.Server/src/mage/server/util/ConfigSettings.java new file mode 100644 index 00000000000..1052a889fb8 --- /dev/null +++ b/Mage.Server/src/mage/server/util/ConfigSettings.java @@ -0,0 +1,90 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server.util; + +import java.io.File; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import mage.server.util.config.Config; +import mage.server.util.config.Plugin; +import mage.util.Logging; +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class ConfigSettings { + + private final static Logger logger = Logging.getLogger(ConfigSettings.class.getName()); + private final static ConfigSettings INSTANCE = new ConfigSettings(); + + private Config config; + + public static ConfigSettings getInstance() { + return INSTANCE; + } + + private ConfigSettings() { + try { + JAXBContext jaxbContext = JAXBContext.newInstance("mage.server.util.config"); + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + config = (Config) unmarshaller.unmarshal(new File("config/config.xml")); + } catch (JAXBException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + + public String getServerName() { + return config.getServer().getServerName(); + } + + public int getPort() { + return config.getServer().getPort().intValue(); + } + + public int getMaxGameThreads() { + return config.getServer().getMaxGameThreads().intValue(); + } + + public int getMaxSecondsIdle() { + return config.getServer().getMaxSecondsIdle().intValue(); + } + + public List getPlayerTypes() { + return config.getPlayerTypes().getPlugin(); + } + + public List getGameTypes() { + return config.getGameTypes().getPlugin(); + } + +} diff --git a/Mage.Server/src/mage/server/util/ThreadExecutor.java b/Mage.Server/src/mage/server/util/ThreadExecutor.java new file mode 100644 index 00000000000..4576d130870 --- /dev/null +++ b/Mage.Server/src/mage/server/util/ThreadExecutor.java @@ -0,0 +1,65 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``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 BetaSteward_at_googlemail.com 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. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.server.util; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class ThreadExecutor { + + private static ExecutorService rmiExecutor = Executors.newCachedThreadPool(); + private static ExecutorService gameExecutor = Executors.newFixedThreadPool(ConfigSettings.getInstance().getMaxGameThreads()); + private static ScheduledExecutorService timeoutExecutor = Executors.newScheduledThreadPool(5); + + private final static ThreadExecutor INSTANCE = new ThreadExecutor(); + + public static ThreadExecutor getInstance() { + return INSTANCE; + } + + private ThreadExecutor() {} + + public ExecutorService getRMIExecutor() { + return rmiExecutor; + } + + public ExecutorService getGameExecutor() { + return gameExecutor; + } + + public ScheduledExecutorService getTimeoutExecutor() { + return timeoutExecutor; + } + +} diff --git a/Mage.Server/src/mage/server/util/resources/config.xml b/Mage.Server/src/mage/server/util/resources/config.xml new file mode 100644 index 00000000000..5b6dbf522bb --- /dev/null +++ b/Mage.Server/src/mage/server/util/resources/config.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/Mage.Server/src/security.policy b/Mage.Server/src/security.policy new file mode 100644 index 00000000000..dda47ba9183 --- /dev/null +++ b/Mage.Server/src/security.policy @@ -0,0 +1,3 @@ +grant { + permission java.security.AllPermission; +}; \ No newline at end of file diff --git a/Mage.Server/xml-resources/jaxb/Config/Config.xsd b/Mage.Server/xml-resources/jaxb/Config/Config.xsd new file mode 100644 index 00000000000..8a7a7c67fa0 --- /dev/null +++ b/Mage.Server/xml-resources/jaxb/Config/Config.xsd @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +