├── nadclient ├── .gitignore ├── binaries │ ├── nadclient-0.1.jar │ ├── nadclient-0.1-javadoc.jar │ └── nadclient-0.1-sources.jar ├── src │ ├── main │ │ └── java │ │ │ └── io │ │ │ └── nadron │ │ │ ├── client │ │ │ ├── util │ │ │ │ ├── Config.java │ │ │ │ ├── Credentials.java │ │ │ │ └── SimpleCredentials.java │ │ │ ├── protocol │ │ │ │ ├── Protocol.java │ │ │ │ └── impl │ │ │ │ │ └── NettyObjectProtocol.java │ │ │ ├── event │ │ │ │ ├── SessionEventHandler.java │ │ │ │ ├── Event.java │ │ │ │ ├── impl │ │ │ │ │ ├── ChangeAttributeEvent.java │ │ │ │ │ ├── StartEventHandler.java │ │ │ │ │ └── DefaultEvent.java │ │ │ │ ├── NetworkEvent.java │ │ │ │ ├── EventHandler.java │ │ │ │ └── EventDispatcher.java │ │ │ ├── app │ │ │ │ ├── PlayerSession.java │ │ │ │ ├── impl │ │ │ │ │ └── DefaultPlayerSession.java │ │ │ │ └── Game.java │ │ │ ├── communication │ │ │ │ ├── DeliveryGuaranty.java │ │ │ │ ├── MessageSender.java │ │ │ │ └── NettyUDPMessageSender.java │ │ │ └── handlers │ │ │ │ └── netty │ │ │ │ ├── UDPEventEncoder.java │ │ │ │ ├── EventObjectEncoder.java │ │ │ │ ├── EventObjectDecoder.java │ │ │ │ ├── MessageBufferEventDecoder.java │ │ │ │ ├── UDPUpstreamHandler.java │ │ │ │ ├── UDPPipelineFactory.java │ │ │ │ ├── DefaultToClientHandler.java │ │ │ │ └── MessageBufferEventEncoder.java │ │ │ └── convert │ │ │ └── Transform.java │ └── test │ │ └── java │ │ └── io │ │ └── nadron │ │ ├── client │ │ ├── util │ │ │ └── LoginHelperTest.java │ │ └── UDPClientTest.java │ │ └── TestClass.java ├── .project ├── .classpath └── README.md ├── jetclient-as3 ├── .gitignore ├── src │ └── main │ │ └── flex │ │ └── io │ │ └── nadron │ │ ├── communication │ │ ├── MessageSender.as │ │ ├── DefaultMessageSender.as │ │ └── MessageBuffer.as │ │ ├── event │ │ ├── NadEvent.as │ │ └── impl │ │ │ └── DefaultEvent.as │ │ ├── codecs │ │ ├── InAndOutCodecChain.as │ │ ├── CodecChain.as │ │ ├── Transform.as │ │ └── impl │ │ │ ├── AMFDeserializer.as │ │ │ ├── LengthFieldPrepender.as │ │ │ ├── AMFSerializer.as │ │ │ ├── MessageBufferEventEncoder.as │ │ │ ├── MessagBufferEventDecoder.as │ │ │ ├── LoginInOutCodecs.as │ │ │ └── DefaultCodecChain.as │ │ ├── protocol │ │ ├── Protocol.as │ │ └── impl │ │ │ ├── AMF3Protocol.as │ │ │ └── MessageBufferProtocol.as │ │ ├── util │ │ └── LoginHelper.as │ │ └── app │ │ └── Session.as └── README.md ├── example-games ├── .gitignore ├── src │ ├── main │ │ └── java │ │ │ └── io │ │ │ └── nadron │ │ │ └── example │ │ │ ├── zombie │ │ │ ├── domain │ │ │ │ ├── Zombie.java │ │ │ │ ├── Defender.java │ │ │ │ ├── IAM.java │ │ │ │ ├── ZombieCommands.java │ │ │ │ ├── World.java │ │ │ │ └── WorldMonitor.java │ │ │ └── game │ │ │ │ ├── Messages.java │ │ │ │ └── ZombieRoom.java │ │ │ ├── lostdecade │ │ │ ├── LDEvent.java │ │ │ └── LDGameState.java │ │ │ └── GameServer.java │ └── test │ │ └── java │ │ └── io │ │ └── nadron │ │ └── example │ │ ├── zombieclient │ │ └── GamePlay.java │ │ └── zombie │ │ ├── TimerCanceller.java │ │ └── WriteByte.java ├── .springBeans ├── .project ├── GameServerLog4j.properties └── log4j2.xml ├── nadron ├── .gitignore ├── lib │ ├── jetlang-0.2.9.jar │ ├── log4j-1.2.16.jar │ ├── msgpack-0.6.8.jar │ ├── aopalliance-1.0.jar │ ├── slf4j-api-1.6.1.jar │ ├── jcl-over-slf4j-1.7.2.jar │ ├── slf4j-log4j12-1.6.1.jar │ ├── blazeds-core-3.2.0.3978.jar │ ├── jackson-core-asl-1.9.12.jar │ ├── netty-all-4.0.10.Final.jar │ ├── blazeds-common-3.2.0.3978.jar │ ├── jackson-mapper-asl-1.9.12.jar │ ├── spring-aop-3.2.2.RELEASE.jar │ ├── spring-beans-3.2.2.RELEASE.jar │ ├── spring-core-3.2.2.RELEASE.jar │ ├── backport-util-concurrent-3.1.jar │ ├── spring-context-3.2.2.RELEASE.jar │ └── spring-expression-3.2.2.RELEASE.jar ├── binaries │ ├── nadron-0.1.jar │ ├── nadron-0.1-javadoc.jar │ └── nadron-0.1-sources.jar ├── src │ ├── test │ │ └── java │ │ │ └── io │ │ │ └── nadron │ │ │ ├── junitcategories │ │ │ ├── Default.java │ │ │ └── Performance.java │ │ │ ├── protocols │ │ │ └── impl │ │ │ │ └── DummyProtocol.java │ │ │ ├── NadronSpringConfig.java │ │ │ ├── util │ │ │ ├── TestGameRoom.java │ │ │ └── SessionHandlerLatchCounter.java │ │ │ └── SpringNettyServer.java │ └── main │ │ ├── java │ │ └── io │ │ │ └── nadron │ │ │ ├── util │ │ │ ├── Credentials.java │ │ │ ├── NadronConfig.java │ │ │ ├── BinaryUtils.java │ │ │ ├── SimpleCredentials.java │ │ │ ├── SmallFileReader.java │ │ │ └── RandomStringGenerator.java │ │ │ ├── concurrent │ │ │ ├── Lane.java │ │ │ ├── ManagedExecutor.java │ │ │ ├── NamedThreadFactory.java │ │ │ ├── JetlangActor.java │ │ │ ├── Lanes.java │ │ │ ├── DefaultLane.java │ │ │ ├── Agent.java │ │ │ ├── DataFlowVariable.java │ │ │ └── Fibers.java │ │ │ ├── event │ │ │ ├── EventHandler.java │ │ │ ├── Event.java │ │ │ ├── ConnectEvent.java │ │ │ ├── impl │ │ │ │ ├── ReconnetEvent.java │ │ │ │ ├── DefaultEventContext.java │ │ │ │ ├── ChangeAttributeEvent.java │ │ │ │ ├── SessionMessageHandler.java │ │ │ │ ├── EventDispatchers.java │ │ │ │ ├── NetworkEventListener.java │ │ │ │ └── DefaultEvent.java │ │ │ ├── JetlangDisposable.java │ │ │ ├── AbstractEventHandler.java │ │ │ ├── NetworkEvent.java │ │ │ ├── EventContext.java │ │ │ ├── SessionEventHandler.java │ │ │ └── EventDispatcher.java │ │ │ ├── service │ │ │ ├── SessionRegistryService.java │ │ │ ├── impl │ │ │ │ ├── SessionRegistry.java │ │ │ │ ├── SimpleUniqueIdGenerator.java │ │ │ │ ├── SimpleLookupService.java │ │ │ │ ├── SimpleGameAdminService.java │ │ │ │ └── SimpleTaskManagerService.java │ │ │ ├── LookupService.java │ │ │ └── UniqueIDGeneratorService.java │ │ │ ├── app │ │ │ ├── SessionFactory.java │ │ │ ├── impl │ │ │ │ ├── InvalidCommandException.java │ │ │ │ └── Sessions.java │ │ │ ├── GameEvent.java │ │ │ ├── GameCommandInterpreter.java │ │ │ ├── Task.java │ │ │ ├── GameStartListener.java │ │ │ ├── Game.java │ │ │ └── PlayerSession.java │ │ │ ├── server │ │ │ ├── ServerManager.java │ │ │ ├── Server.java │ │ │ └── netty │ │ │ │ ├── NettyServer.java │ │ │ │ ├── FlashPolicyServerChannelInitalizer.java │ │ │ │ ├── ProtocolMultiplexerChannelInitializer.java │ │ │ │ ├── UDPChannelInitializer.java │ │ │ │ └── ServerManagerImpl.java │ │ │ ├── handlers │ │ │ ├── netty │ │ │ │ ├── NulEncoder.java │ │ │ │ ├── UDPEventEncoder.java │ │ │ │ ├── EventDecoder.java │ │ │ │ ├── FlashPolicyServerDecoder.java │ │ │ │ ├── EventObjectEncoder.java │ │ │ │ ├── MsgPackDecoder.java │ │ │ │ ├── MsgPackEncoder.java │ │ │ │ ├── TextWebsocketEncoder.java │ │ │ │ ├── EventEncoder.java │ │ │ │ ├── MessageBufferEventDecoder.java │ │ │ │ ├── EventObjectDecoder.java │ │ │ │ ├── JavaObjectToAMF3Encoder.java │ │ │ │ └── MessageBufferEventEncoder.java │ │ │ └── StateAware.java │ │ │ ├── convert │ │ │ ├── Transform.java │ │ │ └── flex │ │ │ │ ├── SerializationContextProvider.java │ │ │ │ ├── AMFDeSerializer.java │ │ │ │ └── AMFSerializer.java │ │ │ ├── communication │ │ │ ├── DeliveryGuaranty.java │ │ │ ├── MessageSender.java │ │ │ └── NettyTCPMessageSender.java │ │ │ ├── protocols │ │ │ ├── AbstractNettyProtocol.java │ │ │ ├── impl │ │ │ │ └── NettyObjectProtocol.java │ │ │ └── Protocol.java │ │ │ └── config │ │ │ └── ServiceBeansConfig.java │ │ └── resources │ │ └── nadron │ │ └── props │ │ ├── nadron.properties │ │ └── flash-policy.xml ├── NadronLog4j.properties ├── .springBeans ├── .project └── log4j2.xml ├── nadclient-js ├── test │ └── images │ │ ├── hero.png │ │ ├── hero2.png │ │ ├── monster.png │ │ └── background.png └── README.md └── .gitignore /nadclient/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /jetclient-as3/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /example-games/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /GameServer.log 3 | -------------------------------------------------------------------------------- /nadron/.gitignore: -------------------------------------------------------------------------------- 1 | /Nadron.log 2 | /target 3 | /.settings -------------------------------------------------------------------------------- /nadron/lib/jetlang-0.2.9.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/lib/jetlang-0.2.9.jar -------------------------------------------------------------------------------- /nadron/lib/log4j-1.2.16.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/lib/log4j-1.2.16.jar -------------------------------------------------------------------------------- /nadron/lib/msgpack-0.6.8.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/lib/msgpack-0.6.8.jar -------------------------------------------------------------------------------- /nadron/binaries/nadron-0.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/binaries/nadron-0.1.jar -------------------------------------------------------------------------------- /nadron/lib/aopalliance-1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/lib/aopalliance-1.0.jar -------------------------------------------------------------------------------- /nadron/lib/slf4j-api-1.6.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/lib/slf4j-api-1.6.1.jar -------------------------------------------------------------------------------- /nadclient-js/test/images/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadclient-js/test/images/hero.png -------------------------------------------------------------------------------- /nadclient-js/test/images/hero2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadclient-js/test/images/hero2.png -------------------------------------------------------------------------------- /nadclient-js/test/images/monster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadclient-js/test/images/monster.png -------------------------------------------------------------------------------- /nadclient/binaries/nadclient-0.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadclient/binaries/nadclient-0.1.jar -------------------------------------------------------------------------------- /nadron/lib/jcl-over-slf4j-1.7.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/lib/jcl-over-slf4j-1.7.2.jar -------------------------------------------------------------------------------- /nadron/lib/slf4j-log4j12-1.6.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/lib/slf4j-log4j12-1.6.1.jar -------------------------------------------------------------------------------- /nadron/src/test/java/io/nadron/junitcategories/Default.java: -------------------------------------------------------------------------------- 1 | package io.nadron.junitcategories; 2 | 3 | public interface Default{} -------------------------------------------------------------------------------- /nadron/binaries/nadron-0.1-javadoc.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/binaries/nadron-0.1-javadoc.jar -------------------------------------------------------------------------------- /nadron/binaries/nadron-0.1-sources.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/binaries/nadron-0.1-sources.jar -------------------------------------------------------------------------------- /nadron/lib/blazeds-core-3.2.0.3978.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/lib/blazeds-core-3.2.0.3978.jar -------------------------------------------------------------------------------- /nadron/lib/jackson-core-asl-1.9.12.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/lib/jackson-core-asl-1.9.12.jar -------------------------------------------------------------------------------- /nadron/lib/netty-all-4.0.10.Final.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/lib/netty-all-4.0.10.Final.jar -------------------------------------------------------------------------------- /nadron/src/test/java/io/nadron/junitcategories/Performance.java: -------------------------------------------------------------------------------- 1 | package io.nadron.junitcategories; 2 | 3 | public interface Performance{} -------------------------------------------------------------------------------- /nadclient-js/test/images/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadclient-js/test/images/background.png -------------------------------------------------------------------------------- /nadron/lib/blazeds-common-3.2.0.3978.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/lib/blazeds-common-3.2.0.3978.jar -------------------------------------------------------------------------------- /nadron/lib/jackson-mapper-asl-1.9.12.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/lib/jackson-mapper-asl-1.9.12.jar -------------------------------------------------------------------------------- /nadron/lib/spring-aop-3.2.2.RELEASE.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/lib/spring-aop-3.2.2.RELEASE.jar -------------------------------------------------------------------------------- /nadron/lib/spring-beans-3.2.2.RELEASE.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/lib/spring-beans-3.2.2.RELEASE.jar -------------------------------------------------------------------------------- /nadron/lib/spring-core-3.2.2.RELEASE.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/lib/spring-core-3.2.2.RELEASE.jar -------------------------------------------------------------------------------- /nadron/lib/backport-util-concurrent-3.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/lib/backport-util-concurrent-3.1.jar -------------------------------------------------------------------------------- /nadron/lib/spring-context-3.2.2.RELEASE.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/lib/spring-context-3.2.2.RELEASE.jar -------------------------------------------------------------------------------- /nadclient/binaries/nadclient-0.1-javadoc.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadclient/binaries/nadclient-0.1-javadoc.jar -------------------------------------------------------------------------------- /nadclient/binaries/nadclient-0.1-sources.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadclient/binaries/nadclient-0.1-sources.jar -------------------------------------------------------------------------------- /nadron/lib/spring-expression-3.2.2.RELEASE.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menacher/java-game-server/HEAD/nadron/lib/spring-expression-3.2.2.RELEASE.jar -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/util/Config.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.util; 2 | 3 | public class Config 4 | { 5 | public static final String RECONNECT_KEY = "RECONNECT_KEY"; 6 | } 7 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/util/Credentials.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.util; 2 | 3 | public interface Credentials 4 | { 5 | abstract String getUsername(); 6 | 7 | abstract String getPassword(); 8 | 9 | } -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/util/Credentials.java: -------------------------------------------------------------------------------- 1 | package io.nadron.util; 2 | 3 | public interface Credentials 4 | { 5 | 6 | public abstract String getUsername(); 7 | 8 | public abstract String getPassword(); 9 | 10 | 11 | } -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/protocol/Protocol.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.protocol; 2 | 3 | import io.nadron.client.app.Session; 4 | 5 | public interface Protocol { 6 | 7 | public void applyProtocol(Session session); 8 | } 9 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/concurrent/Lane.java: -------------------------------------------------------------------------------- 1 | package io.nadron.concurrent; 2 | 3 | public interface Lane 4 | { 5 | boolean isOnSameLane(ID_TYPE currentLane); 6 | ID_TYPE getId(); 7 | UNDERLYING_LANE getUnderlyingLane(); 8 | } 9 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/event/EventHandler.java: -------------------------------------------------------------------------------- 1 | package io.nadron.event; 2 | 3 | 4 | public interface EventHandler 5 | { 6 | /** 7 | * On event 8 | * 9 | * @param event 10 | */ 11 | public void onEvent(Event event); 12 | 13 | public int getEventType(); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /nadron/src/main/resources/nadron/props/nadron.properties: -------------------------------------------------------------------------------- 1 | flash.policyport=843 2 | tcp.port=18090 3 | udp.port=18090 4 | reconnect.delay=300000 5 | bossThreadCount=2 6 | workerThreadCount=2 7 | so.keepalive=true 8 | so.backlog=100 9 | so.sndbuf=65536 10 | so.rcvbuf=65536 11 | so.broadcast=false -------------------------------------------------------------------------------- /jetclient-as3/src/main/flex/io/nadron/communication/MessageSender.as: -------------------------------------------------------------------------------- 1 | package io.nadron.communication 2 | { 3 | import flash.net.Socket; 4 | 5 | /** 6 | * ... 7 | * @author Abraham Menacherry 8 | */ 9 | public interface MessageSender 10 | { 11 | function sendMessage(message:Object):void; 12 | function close():void; 13 | function getSocket():Socket; 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/service/SessionRegistryService.java: -------------------------------------------------------------------------------- 1 | package io.nadron.service; 2 | 3 | import io.nadron.app.Session; 4 | 5 | public interface SessionRegistryService 6 | { 7 | public Session getSession(T key); 8 | 9 | public boolean putSession(T key, Session session); 10 | 11 | public boolean removeSession(T key); 12 | // Add a session type object also to get udp/tcp/any 13 | } 14 | -------------------------------------------------------------------------------- /example-games/src/main/java/io/nadron/example/zombie/domain/Zombie.java: -------------------------------------------------------------------------------- 1 | package io.nadron.example.zombie.domain; 2 | 3 | 4 | public class Zombie 5 | { 6 | private World world; 7 | 8 | public void eatBrains() 9 | { 10 | world.eatBrains(); 11 | } 12 | 13 | public World getWorld() 14 | { 15 | return world; 16 | } 17 | 18 | public void setWorld(World world) 19 | { 20 | this.world = world; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/event/Event.java: -------------------------------------------------------------------------------- 1 | package io.nadron.event; 2 | 3 | public interface Event 4 | { 5 | int getType(); 6 | 7 | void setType(int type); 8 | 9 | Object getSource(); 10 | 11 | void setSource(Object source); 12 | 13 | EventContext getEventContext(); 14 | 15 | void setEventContext(EventContext context); 16 | 17 | long getTimeStamp(); 18 | 19 | void setTimeStamp(long timeStamp); 20 | } 21 | -------------------------------------------------------------------------------- /example-games/src/main/java/io/nadron/example/zombie/domain/Defender.java: -------------------------------------------------------------------------------- 1 | package io.nadron.example.zombie.domain; 2 | 3 | 4 | public class Defender 5 | { 6 | private World world; 7 | 8 | public void shotgun() 9 | { 10 | world.shotgun(); 11 | } 12 | 13 | public World getWorld() 14 | { 15 | return world; 16 | } 17 | 18 | public void setWorld(World world) 19 | { 20 | this.world = world; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/event/ConnectEvent.java: -------------------------------------------------------------------------------- 1 | package io.nadron.event; 2 | 3 | import io.nadron.communication.MessageSender.Fast; 4 | import io.nadron.communication.MessageSender.Reliable; 5 | 6 | public interface ConnectEvent extends Event 7 | { 8 | public Reliable getTcpSender(); 9 | public void setTcpSender(Reliable tcpSender); 10 | public Fast getUdpSender(); 11 | public void setUdpSender(Fast udpSender); 12 | } 13 | -------------------------------------------------------------------------------- /jetclient-as3/src/main/flex/io/nadron/event/NadEvent.as: -------------------------------------------------------------------------------- 1 | package io.nadron.event 2 | { 3 | 4 | /** 5 | * ... 6 | * @author Abraham Menacherry 7 | */ 8 | public interface NadEvent 9 | { 10 | function getType():int 11 | function setType(type:int):void 12 | function getSource():Object 13 | function setSource(source:Object):void; 14 | function getTimestamp():Number 15 | function setTimestamp(timestamp:Number):void 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/event/SessionEventHandler.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.event; 2 | 3 | import io.nadron.client.app.Session; 4 | 5 | /** 6 | * In addition to handling events this handler will also have a reference to the 7 | * session. 8 | * 9 | * @author Abraham Menacherry. 10 | * 11 | */ 12 | public interface SessionEventHandler extends EventHandler 13 | { 14 | Session getSession(); 15 | void setSession(Session session); 16 | } 17 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/event/Event.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.event; 2 | 3 | /** 4 | * An event which will be transmitted to a session, remote server or client. 5 | * 6 | * @author Abraham Menacherry 7 | * 8 | */ 9 | public interface Event 10 | { 11 | int getType(); 12 | 13 | void setType(int type); 14 | 15 | Object getSource(); 16 | 17 | void setSource(Object source); 18 | 19 | long getTimeStamp(); 20 | 21 | void setTimeStamp(long timeStamp); 22 | } 23 | -------------------------------------------------------------------------------- /example-games/.springBeans: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1 4 | 5 | 6 | 7 | 8 | 9 | 10 | src/main/resources/beans/beans.xml 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /jetclient-as3/src/main/flex/io/nadron/codecs/InAndOutCodecChain.as: -------------------------------------------------------------------------------- 1 | package io.nadron.codecs 2 | { 3 | 4 | /** 5 | * A utility interface which has methods to set and get the codec chains. 6 | * @author Abraham Menacherry 7 | */ 8 | public interface InAndOutCodecChain 9 | { 10 | function getInCodecs():CodecChain; 11 | function setInCodecs(inCodecs:CodecChain):void 12 | function getOutCodecs():CodecChain; 13 | function setOutCodecs(outCodecs:CodecChain):void; 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/event/impl/ReconnetEvent.java: -------------------------------------------------------------------------------- 1 | package io.nadron.event.impl; 2 | 3 | import io.nadron.communication.MessageSender.Reliable; 4 | import io.nadron.event.Events; 5 | 6 | public class ReconnetEvent extends DefaultConnectEvent 7 | { 8 | private static final long serialVersionUID = 1L; 9 | 10 | public ReconnetEvent(Reliable tcpSender) 11 | { 12 | super(tcpSender, null); 13 | } 14 | 15 | public int getType() 16 | { 17 | return Events.RECONNECT; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/app/SessionFactory.java: -------------------------------------------------------------------------------- 1 | package io.nadron.app; 2 | 3 | /** 4 | * Used to create sessions. Implementations of this factory can be passed on to 5 | * {@link GameRoom}'s which can then use it to create player sessions during 6 | * login. 7 | * 8 | * @author Abraham Menacherry 9 | * 10 | */ 11 | public interface SessionFactory 12 | { 13 | 14 | public Session newSession(); 15 | 16 | public PlayerSession newPlayerSession(GameRoom gameRoom, Player player); 17 | } 18 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/app/impl/InvalidCommandException.java: -------------------------------------------------------------------------------- 1 | package io.nadron.app.impl; 2 | 3 | public class InvalidCommandException extends Exception 4 | { 5 | /** 6 | * Eclipse generated serial id. 7 | */ 8 | private static final long serialVersionUID = 6458355917188516937L; 9 | 10 | public InvalidCommandException(String message) 11 | { 12 | super(message); 13 | } 14 | public InvalidCommandException(String message, Exception e) 15 | { 16 | super(message,e); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example-games/src/main/java/io/nadron/example/lostdecade/LDEvent.java: -------------------------------------------------------------------------------- 1 | package io.nadron.example.lostdecade; 2 | 3 | import io.nadron.event.impl.DefaultEvent; 4 | 5 | public class LDEvent extends DefaultEvent 6 | { 7 | private static final long serialVersionUID = 1L; 8 | 9 | private LDGameState source; 10 | 11 | @Override 12 | public LDGameState getSource() 13 | { 14 | return source; 15 | } 16 | 17 | public void setSource(LDGameState source) 18 | { 19 | this.source = source; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/util/NadronConfig.java: -------------------------------------------------------------------------------- 1 | package io.nadron.util; 2 | 3 | public class NadronConfig 4 | { 5 | public static final String NODE_NAME = "NadNode"; 6 | public static final String RECONNECT_KEY = "RECONNECT_KEY"; 7 | public static final String RECONNECT_REGISTRY = "RECONNECT_REGISTRY"; 8 | /** 9 | * By default wait for 5 minutes for remote client to reconnect, before 10 | * closing session. 11 | */ 12 | public static final int DEFAULT_RECONNECT_DELAY = 5 * 60 * 1000; 13 | } 14 | -------------------------------------------------------------------------------- /jetclient-as3/src/main/flex/io/nadron/codecs/CodecChain.as: -------------------------------------------------------------------------------- 1 | package io.nadron.codecs 2 | { 3 | 4 | /** 5 | * This interface represents a chain of transformers. 6 | * It will store them in a chain i.e. an Array. Can be 7 | * considered like a composite transform which wraps other 8 | * transforms inside it. 9 | * @author Abraham Menacherry 10 | */ 11 | public interface CodecChain extends Transform 12 | { 13 | function add(next:Transform):void; 14 | function remove(next:Transform):void; 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/server/ServerManager.java: -------------------------------------------------------------------------------- 1 | package io.nadron.server; 2 | 3 | /** 4 | * A generic interface used to manage a server. 5 | * @author Abraham Menacherry 6 | * 7 | */ 8 | public interface ServerManager 9 | { 10 | public void startServers(int tcpPort, int flashPort, int udpPort) throws Exception; 11 | 12 | public void startServers() throws Exception; 13 | /** 14 | * Used to stop the server and manage cleanup of resources. 15 | * 16 | */ 17 | public void stopServers() throws Exception; 18 | } 19 | -------------------------------------------------------------------------------- /nadron/src/main/resources/nadron/props/flash-policy.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/event/JetlangDisposable.java: -------------------------------------------------------------------------------- 1 | package io.nadron.event; 2 | 3 | import org.jetlang.channels.ChannelSubscription; 4 | import org.jetlang.core.Disposable; 5 | 6 | /** 7 | * If the Event dispatcher uses Jetlang internally then it would require to 8 | * dispose of Jetlang {@link ChannelSubscription}s using the dispose method 9 | * during cleanup. 10 | * 11 | * @author Abraham Menacherry 12 | * 13 | */ 14 | public interface JetlangDisposable 15 | { 16 | public Disposable getDisposable(); 17 | 18 | public void setDisposable(Disposable disposable); 19 | } 20 | -------------------------------------------------------------------------------- /nadron/src/test/java/io/nadron/protocols/impl/DummyProtocol.java: -------------------------------------------------------------------------------- 1 | package io.nadron.protocols.impl; 2 | 3 | import io.nadron.app.PlayerSession; 4 | import io.nadron.protocols.Protocol; 5 | 6 | public class DummyProtocol implements Protocol 7 | { 8 | @Override 9 | public String getProtocolName() 10 | { 11 | return null; 12 | } 13 | 14 | @Override 15 | public void applyProtocol(PlayerSession playerSession) 16 | { 17 | 18 | } 19 | 20 | @Override 21 | public void applyProtocol(PlayerSession playerSession, 22 | boolean clearExistingProtocolHandlers) { 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/app/PlayerSession.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.app; 2 | 3 | /** 4 | * This interface abstracts a user session to a {@link Game}. 5 | * 6 | * @author Abraham Menacherry 7 | * 8 | */ 9 | public interface PlayerSession extends Session 10 | { 11 | /** 12 | * Each session is associated with a {@link Player}. This is the actual 13 | * human or machine using this session. 14 | * 15 | * @return Returns that associated Player object or null if it is not 16 | * associated yet. 17 | */ 18 | public abstract Player getPlayer(); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /example-games/src/main/java/io/nadron/example/zombie/domain/IAM.java: -------------------------------------------------------------------------------- 1 | package io.nadron.example.zombie.domain; 2 | 3 | public enum IAM 4 | { 5 | ZOMBIE(1),DEFENDER(2); 6 | IAM(int who){ 7 | 8 | } 9 | 10 | public static IAM getWho(int who) 11 | { 12 | switch (who) 13 | { 14 | case 1: 15 | return ZOMBIE; 16 | case 2: 17 | return DEFENDER; 18 | default: 19 | return ZOMBIE; 20 | } 21 | } 22 | 23 | public static int getInt(IAM iam) 24 | { 25 | switch (iam) 26 | { 27 | case ZOMBIE: 28 | return 1; 29 | case DEFENDER: 30 | return 2; 31 | default: 32 | return 1; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /example-games/src/main/java/io/nadron/example/zombie/game/Messages.java: -------------------------------------------------------------------------------- 1 | package io.nadron.example.zombie.game; 2 | 3 | import io.nadron.example.zombie.domain.ZombieCommands; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.buffer.Unpooled; 6 | 7 | 8 | 9 | /** 10 | * This class acts like a message factory for creating game specific messages. 11 | * @author Abraham Menacherry 12 | * 13 | */ 14 | public class Messages 15 | { 16 | public static ByteBuf apocalypse() 17 | { 18 | ByteBuf buffer = Unpooled.buffer(4); 19 | int cmd = ZombieCommands.APOCALYPSE.getCommand(); 20 | buffer.writeInt(cmd); 21 | return buffer; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /nadclient/.project: -------------------------------------------------------------------------------- 1 | 2 | nadclient 3 | This is a client library for Nadron server https://github.com/menacher/java-game-server/tree/netty4/nadron. Java clients can use this program to connect and interact with nadron server. NO_M2ECLIPSE_SUPPORT: Project files created with the maven-eclipse-plugin are not supported in M2Eclipse. 4 | 5 | 6 | 7 | org.eclipse.jdt.core.javabuilder 8 | 9 | 10 | 11 | org.eclipse.jdt.core.javanature 12 | 13 | -------------------------------------------------------------------------------- /jetclient-as3/src/main/flex/io/nadron/protocol/Protocol.as: -------------------------------------------------------------------------------- 1 | package io.nadron.protocol 2 | { 3 | import flash.net.Socket; 4 | import io.nadron.app.Session; 5 | import io.nadron.communication.MessageSender; 6 | 7 | /** 8 | * A network protocol that is used to connect to remote nadron server. Implementaions will use 9 | * appropriate encoders and decoders based on the protocol implemented by server. 10 | * @author Abraham Menacherry 11 | */ 12 | public interface Protocol 13 | { 14 | function applyProtocol(session:Session):void; 15 | function getName():String; 16 | function createMessageSender(socket:Socket):MessageSender; 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /jetclient-as3/src/main/flex/io/nadron/codecs/Transform.as: -------------------------------------------------------------------------------- 1 | package io.nadron.codecs 2 | { 3 | 4 | /** 5 | * An interface which is implemented by all codecs in nadclient. 6 | * @author Abraham Menacherry 7 | */ 8 | public interface Transform 9 | { 10 | /** 11 | * Transform one object to another. Can be used while encoding a message or decoding a message. 12 | * The classes that implement this interface can be used in "filter chains". 13 | * @param input the object to be converted to another object 14 | * @return This method returns the converted for of the object. 15 | */ 16 | function transform(input:Object):Object; 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/server/Server.java: -------------------------------------------------------------------------------- 1 | package io.nadron.server; 2 | 3 | import java.net.InetSocketAddress; 4 | 5 | public interface Server { 6 | 7 | public interface TransmissionProtocol{ 8 | 9 | } 10 | 11 | public enum TRANSMISSION_PROTOCOL implements TransmissionProtocol { 12 | TCP,UDP; 13 | } 14 | 15 | TransmissionProtocol getTransmissionProtocol(); 16 | 17 | void startServer() throws Exception; 18 | 19 | void startServer(int port) throws Exception;; 20 | 21 | void startServer(InetSocketAddress socketAddress) throws Exception; 22 | 23 | void stopServer() throws Exception; 24 | 25 | InetSocketAddress getSocketAddress(); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/event/impl/ChangeAttributeEvent.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.event.impl; 2 | 3 | public class ChangeAttributeEvent extends DefaultEvent 4 | { 5 | /** 6 | * 7 | */ 8 | private static final long serialVersionUID = -5257419644823465715L; 9 | private String key; 10 | private Object value; 11 | 12 | public String getKey() 13 | { 14 | return key; 15 | } 16 | 17 | public void setKey(String key) 18 | { 19 | this.key = key; 20 | } 21 | 22 | public Object getValue() 23 | { 24 | return value; 25 | } 26 | 27 | public void setValue(Object value) 28 | { 29 | this.value = value; 30 | this.setSource(value); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /nadron/NadronLog4j.properties: -------------------------------------------------------------------------------- 1 | # Set root logger level to DEBUG and its only appender to A1. 2 | 3 | log4j.rootLogger=DEBUG, toLogFile 4 | 5 | log4j.category.io.nadron=TRACE 6 | 7 | # Create appender 'toFile' to send log to 'GameServer.log' file 8 | log4j.appender.toLogFile=org.apache.log4j.RollingFileAppender 9 | log4j.appender.toLogFile.File=Nadron.log 10 | log4j.appender.toLogFile.MaxFileSize=10MB 11 | log4j.appender.toLogFile.MaxBackupIndex=10 12 | log4j.appender.toLogFile.layout=org.apache.log4j.PatternLayout 13 | #log4j.appender.toLogFile.layout.ConversionPattern=%d [%t:%X{LOG_ID},%X{LOG_REQNO},%X{LOG_BEAN}:N%x][%F:%L][%p]:%m%n 14 | log4j.appender.toLogFile.layout.ConversionPattern= %d [%F:%L][%p]:%m%n -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/event/AbstractEventHandler.java: -------------------------------------------------------------------------------- 1 | package io.nadron.event; 2 | 3 | /** 4 | * Abstract event handler is a helper class which must be overriden by classes 5 | * which need to implement the {@link EventHandler} interface. The 6 | * {@link #onEvent(Event)} method needs to be implemented by such classes. 7 | * 8 | * @author Abraham Menacherry 9 | * 10 | */ 11 | public abstract class AbstractEventHandler implements EventHandler 12 | { 13 | private final int EVENT_TYPE; 14 | 15 | public AbstractEventHandler(int eventType) 16 | { 17 | this.EVENT_TYPE = eventType; 18 | } 19 | 20 | @Override 21 | public int getEventType() 22 | { 23 | return EVENT_TYPE; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /nadron/.springBeans: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1 4 | 5 | 6 | 7 | 8 | 9 | 10 | src/main/resources/nadron/beans/netty-handlers.xml 11 | src/main/resources/nadron/beans/server-beans.xml 12 | src/main/resources/nadron/beans/server-protocols.xml 13 | src/main/resources/nadron/beans/service-beans.xml 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/event/NetworkEvent.java: -------------------------------------------------------------------------------- 1 | package io.nadron.event; 2 | 3 | import io.nadron.communication.DeliveryGuaranty; 4 | 5 | /** 6 | * This interface is specifically used for events that will get transmitted to 7 | * remote machine/vm. It contains the {@link DeliveryGuaranty} associated with 8 | * the event so that messages can be transmitted either using TCP or UDP 9 | * transports based on the guaranty defined. Implementations can use RELIABLE as 10 | * default. 11 | * 12 | * @author Abraham Menacherry 13 | * 14 | */ 15 | public interface NetworkEvent extends Event 16 | { 17 | DeliveryGuaranty getDeliveryGuaranty(); 18 | 19 | void setDeliveryGuaranty(DeliveryGuaranty deliveryGuaranty); 20 | } 21 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/handlers/netty/NulEncoder.java: -------------------------------------------------------------------------------- 1 | package io.nadron.handlers.netty; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.Unpooled; 5 | import io.netty.channel.ChannelHandler.Sharable; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.handler.codec.MessageToByteEncoder; 8 | 9 | @Sharable 10 | public class NulEncoder extends MessageToByteEncoder { 11 | 12 | private static final ByteBuf NULL_BUFFER = Unpooled.wrappedBuffer(new byte[] { 0 }); 13 | 14 | @Override 15 | protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) 16 | throws Exception { 17 | out.writeBytes(Unpooled.wrappedBuffer(msg,NULL_BUFFER)); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/util/SimpleCredentials.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.util; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | 5 | public class SimpleCredentials implements Credentials 6 | { 7 | private final String username; 8 | private final String password; 9 | 10 | public SimpleCredentials(ByteBuf buffer) 11 | { 12 | this.username = NettyUtils.readString(buffer); 13 | this.password = NettyUtils.readString(buffer); 14 | } 15 | 16 | @Override 17 | public String getUsername() 18 | { 19 | return username; 20 | } 21 | 22 | @Override 23 | public String getPassword() 24 | { 25 | return password; 26 | } 27 | 28 | @Override 29 | public String toString() 30 | { 31 | return username; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/event/NetworkEvent.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.event; 2 | 3 | import io.nadron.client.communication.DeliveryGuaranty; 4 | 5 | /** 6 | * This interface is specifically used for events that will get transmitted to 7 | * remote machine/vm. It contains the {@link DeliveryGuaranty} associated with 8 | * the event so that messages can be transmitted either using TCP or UDP 9 | * transports based on the guaranty defined. Implementations can use RELIABLE as 10 | * default. 11 | * 12 | * @author Abraham Menacherry 13 | * 14 | */ 15 | public interface NetworkEvent extends Event 16 | { 17 | DeliveryGuaranty getDeliveryGuaranty(); 18 | 19 | void setDeliveryGuaranty(DeliveryGuaranty deliveryGuaranty); 20 | } 21 | -------------------------------------------------------------------------------- /example-games/.project: -------------------------------------------------------------------------------- 1 | 2 | example-games 3 | This project holds some example multiplayer games that uses the Nadron library. The server part is in src/main/java and the client part of the game is in src/test/java. NO_M2ECLIPSE_SUPPORT: Project files created with the maven-eclipse-plugin are not supported in M2Eclipse. 4 | 5 | 6 | 7 | org.eclipse.jdt.core.javabuilder 8 | 9 | 10 | 11 | org.eclipse.jdt.core.javanature 12 | org.springframework.ide.eclipse.core.springnature 13 | org.eclipse.wst.common.project.facet.core.nature 14 | 15 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/event/EventContext.java: -------------------------------------------------------------------------------- 1 | package io.nadron.event; 2 | 3 | import io.nadron.app.Session; 4 | 5 | public interface EventContext 6 | { 7 | Session getSession(); 8 | void setSession(Session session); 9 | 10 | /** 11 | * Retrieves an object which is {@link #setAttachment(Object) attached} to 12 | * this context. 13 | * 14 | * @return {@code null} if no object was attached or 15 | * {@code null} was attached 16 | */ 17 | Object getAttachment(); 18 | 19 | /** 20 | * Attaches an object to this context to store a stateful information 21 | * specific to the {@link Event} which is associated with this 22 | * context. 23 | */ 24 | void setAttachment(Object attachement); 25 | } 26 | -------------------------------------------------------------------------------- /example-games/GameServerLog4j.properties: -------------------------------------------------------------------------------- 1 | # Set root logger level to DEBUG and its only appender to A1. 2 | 3 | log4j.rootLogger=DEBUG, toLogFile 4 | log4j.category.org.springframework=WARN 5 | log4j.category.io.netty=INFO 6 | log4j.category.io.nadron=TRACE 7 | 8 | # Create appender 'toFile' to send log to 'GameServer.log' file 9 | log4j.appender.toLogFile=org.apache.log4j.RollingFileAppender 10 | log4j.appender.toLogFile.File=GameServer.log 11 | log4j.appender.toLogFile.MaxFileSize=10MB 12 | log4j.appender.toLogFile.MaxBackupIndex=10 13 | log4j.appender.toLogFile.layout=org.apache.log4j.PatternLayout 14 | #log4j.appender.toLogFile.layout.ConversionPattern=%d [%t:%X{LOG_ID},%X{LOG_REQNO},%X{LOG_BEAN}:N%x][%F:%L][%p]:%m%n 15 | log4j.appender.toLogFile.layout.ConversionPattern= %d{ISO8601} [%c{1}:%L][%p]:%m%n -------------------------------------------------------------------------------- /nadron/.project: -------------------------------------------------------------------------------- 1 | 2 | nadron 3 | Nadron is a high speed socket based java game server written using Netty and Mike Rettig's Jetlang. It is specifically tuned for network based multiplayer games and supports TCP and UDP network protocols. NO_M2ECLIPSE_SUPPORT: Project files created with the maven-eclipse-plugin are not supported in M2Eclipse. 4 | 5 | 6 | 7 | org.eclipse.jdt.core.javabuilder 8 | 9 | 10 | 11 | org.eclipse.jdt.core.javanature 12 | org.springframework.ide.eclipse.core.springnature 13 | org.eclipse.wst.common.project.facet.core.nature 14 | 15 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/convert/Transform.java: -------------------------------------------------------------------------------- 1 | package io.nadron.convert; 2 | 3 | import io.nadron.communication.MessageBuffer; 4 | 5 | /** 6 | * A generic interface for transforming one object to another. Implementations 7 | * of this interface can be used for decoding and encoding objects, maybe while 8 | * reading from a {@link MessageBuffer} or writing to a Netty channel. 9 | * 10 | * @author Abraham Menacherry 11 | * 12 | * @param 13 | * @param 14 | */ 15 | public interface Transform { 16 | 17 | /** 18 | * Convert Object of type T to type V. 19 | * 20 | * @param object 21 | * The incoming object, mostly a buffer or byte array. 22 | * @return Returns the converted object. 23 | * @throws Exception 24 | */ 25 | V convert(T object) throws Exception; 26 | } 27 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/communication/DeliveryGuaranty.java: -------------------------------------------------------------------------------- 1 | package io.nadron.communication; 2 | 3 | 4 | /** 5 | * The delivery guaranty for the underlying network transport protocol. 6 | * Implementations should be immutable. 7 | * 8 | * @author Abraham Menacherry 9 | * 10 | */ 11 | public interface DeliveryGuaranty 12 | { 13 | public enum DeliveryGuarantyOptions implements DeliveryGuaranty 14 | { 15 | RELIABLE(0),FAST(1); 16 | final int guaranty; 17 | 18 | DeliveryGuarantyOptions(int guaranty) 19 | { 20 | this.guaranty = guaranty; 21 | } 22 | 23 | public int getGuaranty(){ 24 | return guaranty; 25 | } 26 | } 27 | /** 28 | * Return the associated integer guaranty constant. 29 | * 30 | * @return returns the integer guaranty. 31 | */ 32 | public int getGuaranty(); 33 | } 34 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/event/EventHandler.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.event; 2 | 3 | /** 4 | * A handler which can handle a specific event. Implementations of this class 5 | * get attaches to the {@link EventDispatcher}. 6 | * 7 | * @author Abraham Menacherry 8 | * 9 | */ 10 | public interface EventHandler 11 | { 12 | /** 13 | * On event method which will be used to handle an incoming event dispatched 14 | * to it by a {@link EventDispatcher} 15 | * 16 | * @param event 17 | */ 18 | void onEvent(Event event); 19 | 20 | /** 21 | * @return Returns the event type which is an integer. Using this event type 22 | * the event dispatcher can transmit matching events having the same 23 | * event type to this handler instance. 24 | */ 25 | int getEventType(); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/util/BinaryUtils.java: -------------------------------------------------------------------------------- 1 | package io.nadron.util; 2 | 3 | public class BinaryUtils 4 | { 5 | 6 | private static final String HEXES = "0123456789ABCDEF"; 7 | 8 | public static String getHexString(byte[] raw) 9 | { 10 | return getHexString(raw, null); 11 | } 12 | 13 | public static String getHexString(byte[] raw, String separator) 14 | { 15 | boolean sep = (null != separator) && !("".equals(separator)); 16 | 17 | if (raw == null) 18 | { 19 | return null; 20 | } 21 | final StringBuilder hex = new StringBuilder(2 * raw.length); 22 | for (final byte b : raw) 23 | { 24 | hex.append(HEXES.charAt((b & 0xF0) >> 4)).append( 25 | HEXES.charAt((b & 0x0F))); 26 | if (sep) 27 | { 28 | hex.append(separator); 29 | } 30 | } 31 | return hex.toString(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/convert/Transform.java: -------------------------------------------------------------------------------- 1 | package io.nadron.convert; 2 | 3 | import io.nadron.client.communication.MessageBuffer; 4 | 5 | /** 6 | * A generic interface for transforming one object to another. Implementations 7 | * of this interface can be used for decoding and encoding objects, maybe while 8 | * reading from a {@link MessageBuffer} or writing to a Netty channel. 9 | * 10 | * @author Abraham Menacherry 11 | * 12 | * @param 13 | * @param 14 | */ 15 | public interface Transform 16 | { 17 | 18 | /** 19 | * Convert Object of type T to type V. 20 | * 21 | * @param object 22 | * The incoming object, mostly a buffer or byte array. 23 | * @return Returns the converted object. 24 | * @throws Exception 25 | */ 26 | V convert(T object) throws Exception; 27 | } 28 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/event/SessionEventHandler.java: -------------------------------------------------------------------------------- 1 | package io.nadron.event; 2 | 3 | import io.nadron.app.Session; 4 | 5 | /** 6 | * This interface is implemented by event handlers which are listening on 7 | * messages published to a {@link Session}. 8 | * 9 | * @author Abraham Menacherry 10 | * 11 | */ 12 | public interface SessionEventHandler extends EventHandler 13 | { 14 | public Session getSession(); 15 | 16 | /** 17 | * Sets the session instance on this handler. The default implementation 18 | * will throw an exception since the session field is final and is passed in 19 | * via a constructor. 20 | * 21 | * @param session The session instance to set. 22 | * @throws UnsupportedOperationException 23 | */ 24 | public void setSession(Session session) throws UnsupportedOperationException; 25 | } 26 | -------------------------------------------------------------------------------- /nadron/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | %d [%F:%L][%p]:%m%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/app/GameEvent.java: -------------------------------------------------------------------------------- 1 | package io.nadron.app; 2 | 3 | 4 | /** 5 | * This interface has methods that need to be implemented by a game event. Normally 6 | * events would be generated for incoming message, connected, disconnected, 7 | * exception etc. 8 | * 9 | * @author Abraham Menacherry 10 | * 11 | */ 12 | public interface GameEvent { 13 | public T getPayload(); 14 | public void setPayload(T payload); 15 | public PlayerSession getPlayerSession(); 16 | public void setPlayerSession(PlayerSession playerSession); 17 | public O getOpCode(); 18 | public void setOpcode(O opcode); 19 | public E getEventType(); 20 | public void setEventType(E eventType); 21 | public String getEventName(); 22 | public void setEventName(String eventName); 23 | public long getTimeStamp(); 24 | public void setTimeStamp(long timeStamp); 25 | } 26 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/handlers/netty/UDPEventEncoder.java: -------------------------------------------------------------------------------- 1 | package io.nadron.handlers.netty; 2 | 3 | import io.nadron.event.Event; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.channel.ChannelHandler.Sharable; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.channel.socket.DatagramPacket; 8 | 9 | import java.net.InetSocketAddress; 10 | import java.util.List; 11 | 12 | 13 | @Sharable 14 | public class UDPEventEncoder extends MessageBufferEventEncoder 15 | { 16 | @Override 17 | protected void encode(ChannelHandlerContext ctx, Event event, 18 | List out) throws Exception 19 | { 20 | ByteBuf data = (ByteBuf) super.encode(ctx, event); 21 | InetSocketAddress clientAddress = (InetSocketAddress) event 22 | .getEventContext().getAttachment(); 23 | out.add(new DatagramPacket(data, clientAddress)); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/util/SimpleCredentials.java: -------------------------------------------------------------------------------- 1 | package io.nadron.util; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | 5 | public class SimpleCredentials implements Credentials 6 | { 7 | private final String username; 8 | private final String password; 9 | 10 | public SimpleCredentials(String username, String password) 11 | { 12 | this.username = username; 13 | this.password = password; 14 | } 15 | 16 | public SimpleCredentials(ByteBuf buffer) 17 | { 18 | this.username = NettyUtils.readString(buffer); 19 | this.password = NettyUtils.readString(buffer); 20 | } 21 | 22 | @Override 23 | public String getUsername() 24 | { 25 | return username; 26 | } 27 | 28 | @Override 29 | public String getPassword() 30 | { 31 | return password; 32 | } 33 | 34 | @Override 35 | public String toString() 36 | { 37 | return username; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /nadron/src/test/java/io/nadron/NadronSpringConfig.java: -------------------------------------------------------------------------------- 1 | package io.nadron; 2 | 3 | import io.nadron.app.GameRoom; 4 | import io.nadron.config.ServerConfig; 5 | import io.nadron.service.LookupService; 6 | import io.nadron.service.impl.SimpleLookupService; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | import org.springframework.context.annotation.Import; 14 | 15 | @Configuration 16 | @Import(ServerConfig.class) 17 | public class NadronSpringConfig 18 | { 19 | 20 | public @Bean(name="lookupService") LookupService lookupService() 21 | { 22 | Map refKeyGameRoomMap = new HashMap(); 23 | SimpleLookupService service = new SimpleLookupService(refKeyGameRoomMap); 24 | return service; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Maven 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | pom.xml.next 7 | release.properties 8 | dependency-reduced-pom.xml 9 | buildNumber.properties 10 | .mvn/timing.properties 11 | .mvn/wrapper/maven-wrapper.jar 12 | 13 | # Eclipse 14 | .classpath 15 | .project 16 | .settings/ 17 | bin/ 18 | 19 | # IntelliJ IDEA 20 | .idea/ 21 | *.iml 22 | *.iws 23 | *.ipr 24 | out/ 25 | 26 | # NetBeans 27 | nbproject/private/ 28 | build/ 29 | nbbuild/ 30 | dist/ 31 | nbdist/ 32 | .nb-gradle/ 33 | 34 | # VS Code 35 | .vscode/ 36 | 37 | # Log files 38 | *.log 39 | Nadron.log* 40 | GameServer.log* 41 | 42 | # OS files 43 | .DS_Store 44 | Thumbs.db 45 | 46 | # Node.js (for testing) 47 | node_modules/ 48 | package-lock.json 49 | package.json 50 | npm-debug.log* 51 | yarn-debug.log* 52 | yarn-error.log* 53 | 54 | # Temporary files 55 | *.tmp 56 | *.bak 57 | *.swp 58 | *~ 59 | -------------------------------------------------------------------------------- /nadron/src/test/java/io/nadron/util/TestGameRoom.java: -------------------------------------------------------------------------------- 1 | package io.nadron.util; 2 | 3 | import io.nadron.app.PlayerSession; 4 | import io.nadron.app.impl.GameRoomSession; 5 | 6 | import java.util.concurrent.CountDownLatch; 7 | import java.util.concurrent.atomic.AtomicLong; 8 | 9 | 10 | 11 | public class TestGameRoom extends GameRoomSession { 12 | 13 | private final AtomicLong counter; 14 | private final CountDownLatch latch; 15 | 16 | public TestGameRoom(GameRoomSessionBuilder gameRoomSessionBuilder, 17 | AtomicLong counter, CountDownLatch latch) { 18 | super(gameRoomSessionBuilder); 19 | this.counter = counter; 20 | this.latch = latch; 21 | } 22 | 23 | @Override 24 | public void onLogin(PlayerSession playerSession) { 25 | SessionHandlerLatchCounter handler = new SessionHandlerLatchCounter( 26 | playerSession, counter, latch); 27 | playerSession.addHandler(handler); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/handlers/netty/EventDecoder.java: -------------------------------------------------------------------------------- 1 | package io.nadron.handlers.netty; 2 | 3 | import io.nadron.event.Events; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.channel.ChannelHandler.Sharable; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.handler.codec.MessageToMessageDecoder; 8 | 9 | import java.util.List; 10 | 11 | 12 | 13 | @Sharable 14 | public class EventDecoder extends MessageToMessageDecoder 15 | { 16 | @Override 17 | protected void decode(ChannelHandlerContext ctx, ByteBuf msg, 18 | List out) throws Exception 19 | { 20 | int opcode = msg.readUnsignedByte(); 21 | if (Events.LOG_IN == opcode || Events.RECONNECT == opcode) 22 | { 23 | msg.readUnsignedByte();// To read-destroy the protocol version byte. 24 | } 25 | ByteBuf buffer = msg.readBytes(msg.readableBytes()); 26 | out.add(Events.event(buffer, opcode)); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/concurrent/ManagedExecutor.java: -------------------------------------------------------------------------------- 1 | package io.nadron.concurrent; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.concurrent.ExecutorService; 6 | import java.util.concurrent.Executors; 7 | import java.util.concurrent.ThreadFactory; 8 | 9 | public class ManagedExecutor 10 | { 11 | public static final List EXECUTOR_SERVICES = new ArrayList(); 12 | 13 | static{ 14 | Runtime.getRuntime().addShutdownHook(new Thread(){ 15 | @Override 16 | public void run() 17 | { 18 | for(ExecutorService service: EXECUTOR_SERVICES){ 19 | service.shutdown(); 20 | } 21 | } 22 | }); 23 | } 24 | 25 | public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) 26 | { 27 | final ExecutorService exec = Executors.newSingleThreadExecutor(threadFactory); 28 | EXECUTOR_SERVICES.add(exec); 29 | return exec; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/app/GameCommandInterpreter.java: -------------------------------------------------------------------------------- 1 | package io.nadron.app; 2 | 3 | import io.nadron.app.impl.InvalidCommandException; 4 | 5 | /** 6 | * This interface defines a command interpreter, which will basically map the 7 | * incoming bytes to a method call on some class. 8 | * 9 | * @author Abraham Menacherry 10 | * 11 | */ 12 | public interface GameCommandInterpreter 13 | { 14 | /** 15 | * A generic method which can be used to interpret an incoming command from 16 | * the client. The implementation would have a number of switch-case or 17 | * if-else statements whereby command bytes can be transformed in actual 18 | * java method calls. 19 | * 20 | * @param command 21 | * Should mostly be of type {@link GameEvent}, but flexibility 22 | * is provided to user to send in anything to the interpreter. 23 | */ 24 | public void interpretCommand(Object command) throws InvalidCommandException; 25 | } 26 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/event/impl/DefaultEventContext.java: -------------------------------------------------------------------------------- 1 | package io.nadron.event.impl; 2 | 3 | import io.nadron.app.Session; 4 | import io.nadron.event.EventContext; 5 | 6 | public class DefaultEventContext implements EventContext 7 | { 8 | 9 | private Object attachement; 10 | private Session session; 11 | 12 | public DefaultEventContext() 13 | { 14 | 15 | } 16 | 17 | public DefaultEventContext(Session session, Object attachement) 18 | { 19 | this.session = session; 20 | this.attachement = attachement; 21 | } 22 | 23 | @Override 24 | public Object getAttachment() 25 | { 26 | return attachement; 27 | } 28 | 29 | @Override 30 | public Session getSession() 31 | { 32 | return session; 33 | } 34 | 35 | @Override 36 | public void setAttachment(Object attachement) 37 | { 38 | this.attachement = attachement; 39 | } 40 | 41 | @Override 42 | public void setSession(Session session) 43 | { 44 | this.session = session; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/util/SmallFileReader.java: -------------------------------------------------------------------------------- 1 | package io.nadron.util; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileReader; 6 | import java.io.IOException; 7 | 8 | public class SmallFileReader 9 | { 10 | public static String readSmallFile(String filePath) 11 | throws IOException 12 | { 13 | if(filePath == null) 14 | { 15 | return null; 16 | } 17 | File smallFile = new File(filePath); 18 | return readSmallFile(smallFile); 19 | } 20 | 21 | public static String readSmallFile(File smallFile) 22 | throws IOException 23 | { 24 | FileReader reader = new FileReader(smallFile); 25 | BufferedReader bufferedReader = new BufferedReader(reader); 26 | StringBuffer buf = new StringBuffer(); 27 | String line = null; 28 | while ((line = bufferedReader.readLine()) != null) 29 | { 30 | buf.append(line); 31 | } 32 | bufferedReader.close(); 33 | reader.close(); 34 | return buf.toString(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/app/impl/DefaultPlayerSession.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.app.impl; 2 | 3 | import io.nadron.client.app.Player; 4 | import io.nadron.client.app.PlayerSession; 5 | import io.nadron.client.event.Event; 6 | 7 | /** 8 | * This implementation of the {@link PlayerSession} interface is used to both 9 | * receive and send messages to a particular player using the 10 | * {@link Event}{@link #onEvent(Event)}. 11 | * 12 | * @author Abraham Menacherry 13 | * 14 | */ 15 | public class DefaultPlayerSession extends DefaultSession implements PlayerSession 16 | { 17 | 18 | /** 19 | * Each player session belongs to a Player. This variable holds the 20 | * reference. 21 | */ 22 | final protected Player player; 23 | 24 | protected DefaultPlayerSession(SessionBuilder sessionBuilder, Player player) 25 | { 26 | super(sessionBuilder); 27 | this.player = player; 28 | } 29 | 30 | @Override 31 | public Player getPlayer() 32 | { 33 | return player; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/app/Task.java: -------------------------------------------------------------------------------- 1 | package io.nadron.app; 2 | 3 | import io.nadron.service.TaskManagerService; 4 | 5 | /** 6 | * Represents a task that can be executed in the game system. Any class that 7 | * implements this interface and submits instances to the 8 | * {@link TaskManagerService} instance will be managed by the container. It 9 | * will automatically store the task such that restarts of the server do not 10 | * stop recurring tasks from stopping. In future, this may also be used for 11 | * sending tasks from one server node to another during node shutdown etc. 12 | * 13 | * @author Abraham Menacherry 14 | * 15 | */ 16 | public interface Task extends Runnable 17 | { 18 | /** 19 | * @return returns the unique task id of the task. For future 20 | * implementations, this value has to be unique across multiple 21 | * server nodes. 22 | */ 23 | Object getId(); 24 | 25 | /** 26 | * @param id 27 | * Set the unique task id. 28 | */ 29 | void setId(Object id); 30 | } 31 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/communication/DeliveryGuaranty.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.communication; 2 | 3 | /** 4 | * The delivery guaranty for the underlying network transport protocol. 5 | * Implementations should be immutable. 6 | * 7 | * @author Abraham Menacherry 8 | * 9 | */ 10 | public interface DeliveryGuaranty 11 | { 12 | /** 13 | * An enumeration which implements {@link DeliveryGuaranty}. Used to 14 | * abstract out the TCP and UDP transports. 15 | * 16 | * @author Abraham Menacherry 17 | * 18 | */ 19 | public enum DeliveryGuarantyOptions implements DeliveryGuaranty 20 | { 21 | RELIABLE(1), FAST(2); 22 | final int guaranty; 23 | 24 | DeliveryGuarantyOptions(int guaranty) 25 | { 26 | this.guaranty = guaranty; 27 | } 28 | 29 | public int getGuaranty() 30 | { 31 | return guaranty; 32 | } 33 | } 34 | 35 | /** 36 | * Return the associated integer guaranty constant. 37 | * 38 | * @return returns the integer guaranty. 39 | */ 40 | public int getGuaranty(); 41 | } 42 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/handlers/netty/FlashPolicyServerDecoder.java: -------------------------------------------------------------------------------- 1 | package io.nadron.handlers.netty; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.Unpooled; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.handler.codec.ReplayingDecoder; 7 | import io.netty.util.CharsetUtil; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @author Bruce Mitchener 13 | */ 14 | public class FlashPolicyServerDecoder extends ReplayingDecoder { 15 | // We don't check for the trailing NULL to make telnet-based debugging easier. 16 | private final ByteBuf requestBuffer = Unpooled.copiedBuffer("", CharsetUtil.US_ASCII); 17 | 18 | @Override 19 | protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, 20 | List out) throws Exception 21 | { 22 | ByteBuf data = buffer.readBytes(requestBuffer.readableBytes()); 23 | if (data.equals(requestBuffer)) { 24 | out.add(data); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /example-games/src/main/java/io/nadron/example/zombie/domain/ZombieCommands.java: -------------------------------------------------------------------------------- 1 | package io.nadron.example.zombie.domain; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public enum ZombieCommands 7 | { 8 | SHOT_GUN(1),EAT_BRAINS(2),SELECT_TEAM(3),APOCALYPSE(4),UNKNOWN(-1); 9 | final int command; 10 | 11 | ZombieCommands(int cmd){ 12 | this.command = cmd; 13 | } 14 | 15 | public int getCommand() 16 | { 17 | return command; 18 | } 19 | 20 | public static class CommandsEnum 21 | { 22 | private static final Map INT_COMMAND_MAP; 23 | static { 24 | INT_COMMAND_MAP = new HashMap(); 25 | for(ZombieCommands command: ZombieCommands.values()){ 26 | INT_COMMAND_MAP.put(command.getCommand(), command); 27 | } 28 | } 29 | 30 | public static ZombieCommands fromInt(Integer i) 31 | { 32 | ZombieCommands command = INT_COMMAND_MAP.get(i); 33 | if(null == command){ 34 | command = ZombieCommands.UNKNOWN; 35 | } 36 | return command; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /jetclient-as3/src/main/flex/io/nadron/codecs/impl/AMFDeserializer.as: -------------------------------------------------------------------------------- 1 | package io.nadron.codecs.impl 2 | { 3 | import flash.utils.ByteArray; 4 | import io.nadron.codecs.Transform; 5 | import io.nadron.event.Events; 6 | 7 | /** 8 | * This decoder will convert an incoming ByteArray into a NadEvent. It will first read the opcode 9 | * byte to see which kind of event is coming from remote nadron server, say SESSION_MESSAGE and it will 10 | * read the payload into an object using byte array's readObject method. 11 | * @author Abraham Menacherry 12 | */ 13 | public class AMFDeserializer implements Transform 14 | { 15 | 16 | public function AMFDeserializer() 17 | { 18 | 19 | } 20 | 21 | public function transform(input:Object):Object 22 | { 23 | var bytes:ByteArray = input as ByteArray; 24 | var eventType:int = bytes.readByte(); 25 | if (eventType == Events.NETWORK_MESSAGE) 26 | { 27 | eventType = Events.SESSION_MESSAGE; 28 | } 29 | return Events.event(eventType, bytes.readObject()); 30 | } 31 | 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /jetclient-as3/src/main/flex/io/nadron/codecs/impl/LengthFieldPrepender.as: -------------------------------------------------------------------------------- 1 | package io.nadron.codecs.impl 2 | { 3 | import flash.utils.ByteArray; 4 | import io.nadron.codecs.Transform; 5 | 6 | /** 7 | * Whenever a message is sent to remote nadron server the length of the number of bytes 8 | * needs to be prepended to the message. This encoder will do that. It will accept a 9 | * byte array, find its length, create another byte array of the form 10 | * and return it. Normally this is the last encoder before a message is written to a socket. 11 | * @author Abraham Menacherry 12 | */ 13 | public class LengthFieldPrepender implements Transform 14 | { 15 | 16 | public function LengthFieldPrepender() 17 | { 18 | 19 | } 20 | 21 | public function transform(input:Object):Object 22 | { 23 | var message:ByteArray = input as ByteArray; 24 | var buffer:ByteArray = new ByteArray; 25 | buffer.writeShort(message.length); 26 | buffer.writeBytes(message); 27 | return buffer; 28 | } 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/handlers/StateAware.java: -------------------------------------------------------------------------------- 1 | package io.nadron.handlers; 2 | 3 | import io.nadron.service.GameStateManagerService; 4 | 5 | /** 6 | * Some handlers need to know the game state. This interface will be implemented 7 | * by such handlers so that there is a uniform way to get and set state on these 8 | * handlers. 9 | * 10 | * @author Abraham Menacherry. 11 | * 12 | */ 13 | public interface StateAware 14 | { 15 | /** 16 | * This method is used to get the state manager associated with this 17 | * handler. 18 | * 19 | * @return Returns the associated state manager instance, or null if none 20 | * are associated. 21 | */ 22 | public GameStateManagerService getGameStateManagerService(); 23 | 24 | /** 25 | * Method used to set the game state manager service on a state aware 26 | * handler. 27 | * 28 | * @param gameStateManagerService 29 | * The state manager instance to set. 30 | */ 31 | public void setGameStateManagerService( 32 | GameStateManagerService gameStateManagerService); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/service/impl/SessionRegistry.java: -------------------------------------------------------------------------------- 1 | package io.nadron.service.impl; 2 | 3 | import io.nadron.app.Session; 4 | import io.nadron.service.SessionRegistryService; 5 | 6 | import java.util.Map; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | 9 | 10 | 11 | public class SessionRegistry implements SessionRegistryService 12 | { 13 | protected final Map sessions; 14 | 15 | public SessionRegistry() 16 | { 17 | sessions = new ConcurrentHashMap(1000); 18 | } 19 | 20 | @Override 21 | public Session getSession(T key) 22 | { 23 | return sessions.get(key); 24 | } 25 | 26 | @Override 27 | public boolean putSession(T key, Session session) 28 | { 29 | if(null == key || null == session) 30 | { 31 | return false; 32 | } 33 | 34 | if(null == sessions.put(key, session)) 35 | { 36 | return true; 37 | } 38 | return false; 39 | } 40 | 41 | @Override 42 | public boolean removeSession(Object key) 43 | { 44 | return null != sessions.remove(key) ? true : false; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/event/impl/ChangeAttributeEvent.java: -------------------------------------------------------------------------------- 1 | package io.nadron.event.impl; 2 | 3 | import io.nadron.event.Events; 4 | 5 | public class ChangeAttributeEvent extends DefaultEvent 6 | { 7 | private static final long serialVersionUID = -5257419644823465715L; 8 | private String key; 9 | private Object value; 10 | 11 | public ChangeAttributeEvent(String key, Object value) 12 | { 13 | this.key = key; 14 | this.value = value; 15 | } 16 | 17 | @Override 18 | public int getType() { 19 | return Events.CHANGE_ATTRIBUTE; 20 | } 21 | 22 | public String getKey() 23 | { 24 | return key; 25 | } 26 | 27 | public void setKey(String key) 28 | { 29 | this.key = key; 30 | } 31 | 32 | public Object getValue() 33 | { 34 | return value; 35 | } 36 | 37 | public void setValue(Object value) 38 | { 39 | this.value = value; 40 | this.setSource(value); 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | return "ChangeAttributeEvent [key=" + key + ", value=" + value 46 | + ", type=" + type + "]"; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/event/impl/StartEventHandler.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.event.impl; 2 | 3 | import io.nadron.client.app.Session; 4 | import io.nadron.client.event.Events; 5 | import io.nadron.client.event.SessionEventHandler; 6 | 7 | /** 8 | * A handler to handle "START" event sent by the server. It is after the start 9 | * event that other changes for e.g. to protocol or sending of data from client 10 | * should be done. 11 | * 12 | * @author Abraham Menacherry 13 | * 14 | */ 15 | public abstract class StartEventHandler implements SessionEventHandler 16 | { 17 | 18 | private final Session session; 19 | 20 | public StartEventHandler(Session session) 21 | { 22 | this.session = session; 23 | } 24 | 25 | @Override 26 | public int getEventType() 27 | { 28 | return Events.START; 29 | } 30 | 31 | @Override 32 | public Session getSession() { 33 | return session; 34 | } 35 | 36 | @Override 37 | public void setSession(Session session) { 38 | throw new UnsupportedOperationException("Cannot set session again"); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/service/impl/SimpleUniqueIdGenerator.java: -------------------------------------------------------------------------------- 1 | package io.nadron.service.impl; 2 | 3 | import io.nadron.service.UniqueIDGeneratorService; 4 | import io.nadron.util.NadronConfig; 5 | 6 | import java.util.concurrent.atomic.AtomicLong; 7 | 8 | 9 | /** 10 | * Uses an atomic long to increment and provide a unique id. This will not work 11 | * in case of clustered servers. 12 | * 13 | * @author Abraham.Menacherry 14 | * 15 | */ 16 | public class SimpleUniqueIdGenerator implements UniqueIDGeneratorService 17 | { 18 | public static final AtomicLong ID = new AtomicLong(0l); 19 | 20 | @Override 21 | public Object generate() 22 | { 23 | String nodeName = System.getProperty(NadronConfig.NODE_NAME); 24 | if (null == nodeName || "".equals(nodeName)) 25 | { 26 | return ID.incrementAndGet(); 27 | } 28 | else 29 | { 30 | return nodeName + ID.incrementAndGet(); 31 | } 32 | } 33 | 34 | @Override 35 | public Object generateFor(@SuppressWarnings("rawtypes") Class klass) 36 | { 37 | return klass.getSimpleName() + ID.incrementAndGet(); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/app/impl/Sessions.java: -------------------------------------------------------------------------------- 1 | package io.nadron.app.impl; 2 | 3 | import io.nadron.app.GameRoom; 4 | import io.nadron.app.Player; 5 | import io.nadron.app.PlayerSession; 6 | import io.nadron.app.Session; 7 | import io.nadron.app.SessionFactory; 8 | import io.nadron.app.impl.DefaultPlayerSession.PlayerSessionBuilder; 9 | import io.nadron.app.impl.DefaultSession.SessionBuilder; 10 | 11 | 12 | /** 13 | * Factory class used to create a {@link PlayerSession} instance. It will 14 | * create a new instance, initialize it and set the {@link GameRoom} reference 15 | * if necessary. 16 | * 17 | * @author Abraham Menacherry 18 | * 19 | */ 20 | public class Sessions implements SessionFactory 21 | { 22 | 23 | public static final SessionFactory INSTANCE = new Sessions(); 24 | 25 | @Override 26 | public Session newSession() 27 | { 28 | return new SessionBuilder().build(); 29 | } 30 | 31 | @Override 32 | public PlayerSession newPlayerSession(GameRoom gameRoom, Player player) 33 | { 34 | return new PlayerSessionBuilder().parentGameRoom(gameRoom).player(player).build(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /jetclient-as3/src/main/flex/io/nadron/codecs/impl/AMFSerializer.as: -------------------------------------------------------------------------------- 1 | package io.nadron.codecs.impl 2 | { 3 | import flash.utils.ByteArray; 4 | import io.nadron.codecs.Transform; 5 | import io.nadron.event.NadEvent; 6 | 7 | /** 8 | * Converts an incoming NadEvent into a ByteArray. It will read the event type from the 9 | * NadEvent. Then it will serialize the payload an object to byte array. Both the event 10 | * type and serialized bytes are written to a byte array and send to next encoder in the chain. 11 | * @author Abraham Menacherry 12 | */ 13 | public class AMFSerializer implements Transform 14 | { 15 | 16 | public function AMFSerializer() 17 | { 18 | 19 | } 20 | 21 | public function transform(input:Object):Object 22 | { 23 | if(input == null){ 24 | throw new Error("null isn't a legal serialization candidate"); 25 | } 26 | var nEvent:NadEvent = input as NadEvent; 27 | var bytes:ByteArray = new ByteArray(); 28 | bytes.writeByte(nEvent.getType()); 29 | bytes.writeObject(nEvent.getSource()); 30 | bytes.position = 0; 31 | return bytes; 32 | } 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /nadclient/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/handlers/netty/EventObjectEncoder.java: -------------------------------------------------------------------------------- 1 | package io.nadron.handlers.netty; 2 | 3 | import io.nadron.event.Event; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.handler.codec.MessageToMessageEncoder; 7 | import io.netty.handler.codec.serialization.ObjectEncoder; 8 | 9 | import java.io.Serializable; 10 | import java.util.List; 11 | 12 | public class EventObjectEncoder extends MessageToMessageEncoder 13 | { 14 | @Override 15 | protected void encode(ChannelHandlerContext ctx, Event event, List out) 16 | throws Exception { 17 | ByteBuf data = ctx.alloc().buffer(); 18 | data.writeByte(event.getType()); 19 | if (null != event.getSource()) 20 | { 21 | new SourceEncoder().encode(ctx, (Serializable)event.getSource(), data); 22 | } 23 | out.add(data); 24 | } 25 | 26 | public static class SourceEncoder extends ObjectEncoder 27 | { 28 | @Override 29 | protected void encode(ChannelHandlerContext ctx, Serializable msg, 30 | ByteBuf out) throws Exception { 31 | super.encode(ctx, msg, out); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /jetclient-as3/README.md: -------------------------------------------------------------------------------- 1 | **NOTE**: This client can connect to JetServer but the coding, documenation etc are still work in progress. 2 | This is a client project for [jetserver](https://github.com/menacher/java-game-server/tree/master/jetserver) library. An example main class is provided in src/main/flex org.menacheri package. 3 | 4 | About the Client 5 | ================ 6 | Execute Main.as from IDE and it will connect to remote jetserver and start receiving events. Assumption is that JetServer is running and Main.as is using accurate hostname and port number. 7 | 8 | Usage as game client 9 | ==================== 10 | The general usage steps could be as outlined below. 11 | 1. Create a loginHelper. 12 | 2. Create a session factory using this login helper instance. 13 | 3. Create as many sessions as required using this factory. Generally only one is required for a client. 14 | 4. Check the code in Main.as for more information on how to add an event listener to session to receive events. 15 | 5. Main.as also has code on how to write back to JetServer. Mostly it is done using session.sendToServer(JetEvent). 16 | 17 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/event/impl/SessionMessageHandler.java: -------------------------------------------------------------------------------- 1 | package io.nadron.event.impl; 2 | 3 | import io.nadron.app.Session; 4 | import io.nadron.event.Events; 5 | import io.nadron.event.SessionEventHandler; 6 | 7 | /** 8 | * This abstract helper class can be used to quickly create a listener which 9 | * listens for SESSION_MESSAGE events. Child classes need to override the 10 | * onEvent to plugin the logic. 11 | * 12 | * @author Abraham Menacherry 13 | * 14 | */ 15 | public abstract class SessionMessageHandler implements SessionEventHandler { 16 | 17 | private final Session session; 18 | 19 | public SessionMessageHandler(Session session) 20 | { 21 | this.session = session; 22 | } 23 | 24 | @Override 25 | public int getEventType() { 26 | return Events.SESSION_MESSAGE; 27 | } 28 | 29 | @Override 30 | public Session getSession() { 31 | return session; 32 | } 33 | 34 | @Override 35 | public void setSession(Session session) 36 | throws UnsupportedOperationException { 37 | throw new UnsupportedOperationException( 38 | "Session instance is final and cannot be reset on this handler"); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /example-games/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | %d{ISO8601} [%c{1}:%L][%p]:%m%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /jetclient-as3/src/main/flex/io/nadron/event/impl/DefaultEvent.as: -------------------------------------------------------------------------------- 1 | package io.nadron.event.impl 2 | { 3 | import flash.events.Event; 4 | import io.nadron.event.NadEvent; 5 | 6 | /** 7 | * ... 8 | * @author Abraham Menacherry 9 | */ 10 | public class DefaultEvent extends Event implements NadEvent 11 | { 12 | private var nEventType:int; 13 | private var source:Object; 14 | private var timestamp:Number; 15 | 16 | public function DefaultEvent(eventType:String) 17 | { 18 | super(eventType); 19 | } 20 | 21 | public function getType():int 22 | { 23 | return nEventType; 24 | } 25 | 26 | public function setType(type:int):void 27 | { 28 | this.nEventType = type; 29 | } 30 | 31 | public function getSource():Object 32 | { 33 | return source; 34 | } 35 | 36 | public function setSource(eventSource:Object):void 37 | { 38 | this.source = eventSource; 39 | } 40 | 41 | public function getTimestamp():Number 42 | { 43 | return timestamp; 44 | } 45 | 46 | public function setTimestamp(eventTimestamp:Number):void 47 | { 48 | this.timestamp = eventTimestamp; 49 | } 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /jetclient-as3/src/main/flex/io/nadron/codecs/impl/MessageBufferEventEncoder.as: -------------------------------------------------------------------------------- 1 | package io.nadron.codecs.impl 2 | { 3 | import flash.utils.ByteArray; 4 | import io.nadron.codecs.Transform; 5 | import io.nadron.communication.MessageBuffer; 6 | import io.nadron.event.NadEvent; 7 | /** 8 | * Converts an incoming NadEvent into a ByteArray. It will read the event type and 9 | * the source of the event and write them to a ByteArray. 10 | * @author Abraham Menacherry 11 | */ 12 | public class MessageBufferEventEncoder implements Transform 13 | { 14 | import io.nadron.event.Events; 15 | 16 | public function MessageBufferEventEncoder() 17 | { 18 | 19 | } 20 | 21 | public function transform(input:Object):Object 22 | { 23 | var event:NadEvent = input as NadEvent; 24 | var message:ByteArray = new ByteArray(); 25 | var opCode:int = event.getType(); 26 | message.writeByte(opCode); 27 | if (opCode == Events.LOG_IN) { 28 | message.writeByte(Events.JET_PROTOCOL); 29 | } 30 | var messageBuffer:MessageBuffer = event.getSource() as MessageBuffer 31 | message.writeBytes(messageBuffer.getBuffer()); 32 | return message; 33 | } 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/concurrent/NamedThreadFactory.java: -------------------------------------------------------------------------------- 1 | package io.nadron.concurrent; 2 | 3 | import java.util.concurrent.ThreadFactory; 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | 6 | public class NamedThreadFactory implements ThreadFactory 7 | { 8 | private static AtomicInteger counter = new AtomicInteger(1); 9 | private String name = "Lane"; 10 | private boolean daemon; 11 | private int priority; 12 | 13 | public NamedThreadFactory(String name) { 14 | this(name, false, -1); 15 | } 16 | 17 | public NamedThreadFactory(String name, boolean daemon) { 18 | this(name, daemon, -1); 19 | } 20 | 21 | public NamedThreadFactory(String name, boolean daemon, int priority) { 22 | this.name = name; 23 | this.daemon = daemon; 24 | this.priority = priority; 25 | } 26 | 27 | @Override 28 | public Thread newThread(Runnable r) { 29 | Thread thread = new Thread(r, name + "[" + counter.getAndIncrement() + "]"); 30 | thread.setDaemon(daemon); 31 | if (priority != -1) { 32 | thread.setPriority(priority); 33 | } 34 | return thread; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/event/impl/EventDispatchers.java: -------------------------------------------------------------------------------- 1 | package io.nadron.event.impl; 2 | 3 | import io.nadron.app.GameRoom; 4 | import io.nadron.concurrent.Fibers; 5 | import io.nadron.concurrent.Lane; 6 | import io.nadron.concurrent.LaneStrategy; 7 | import io.nadron.event.Event; 8 | import io.nadron.event.EventDispatcher; 9 | 10 | import java.util.concurrent.ExecutorService; 11 | 12 | import org.jetlang.channels.MemoryChannel; 13 | import org.jetlang.fibers.Fiber; 14 | 15 | public class EventDispatchers 16 | { 17 | public static EventDispatcher newJetlangEventDispatcher(GameRoom room, 18 | LaneStrategy strategy) 19 | { 20 | Fiber fiber = null; 21 | JetlangEventDispatcher dispatcher = null; 22 | if (null == room) 23 | { 24 | fiber = Fibers.pooledFiber(); 25 | dispatcher = new JetlangEventDispatcher(new MemoryChannel(), 26 | fiber, null); 27 | } 28 | else 29 | { 30 | Lane lane = strategy.chooseLane(room); 31 | fiber = Fibers.pooledFiber(lane); 32 | dispatcher = new JetlangEventDispatcher(new MemoryChannel(), 33 | fiber, lane); 34 | } 35 | dispatcher.initialize(); 36 | 37 | return dispatcher; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/handlers/netty/UDPEventEncoder.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.handlers.netty; 2 | 3 | import io.nadron.client.event.Event; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.channel.ChannelHandler.Sharable; 7 | import io.netty.channel.socket.DatagramPacket; 8 | 9 | import java.net.InetSocketAddress; 10 | import java.util.List; 11 | 12 | 13 | @Sharable 14 | public class UDPEventEncoder extends MessageBufferEventEncoder 15 | { 16 | private InetSocketAddress udpServerAddress; 17 | 18 | public UDPEventEncoder(InetSocketAddress udpServerAddress) 19 | { 20 | this.udpServerAddress = udpServerAddress; 21 | } 22 | 23 | @Override 24 | protected void encode(ChannelHandlerContext ctx, Event event, 25 | List out) throws Exception 26 | { 27 | ByteBuf data = (ByteBuf)super.encode(ctx, event); 28 | out.add(new DatagramPacket(data, udpServerAddress)); 29 | ctx.flush(); 30 | } 31 | 32 | public InetSocketAddress getUdpServerAddress() 33 | { 34 | return udpServerAddress; 35 | } 36 | public void setUdpServerAddress(InetSocketAddress udpServerAddress) 37 | { 38 | this.udpServerAddress = udpServerAddress; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/app/Game.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.app; 2 | 3 | import io.netty.channel.ChannelFuture; 4 | 5 | /** 6 | * This interface abstracts a game domain object. Each game deployed in the 7 | * server should implement this interface. 8 | * 9 | * @author Abraham Menacherry 10 | * 11 | */ 12 | public interface Game 13 | { 14 | /** 15 | * @return Returns the unique id associated with this game object. 16 | */ 17 | public Object getId(); 18 | 19 | /** 20 | * @param id 21 | * Sets the unique id for this game. 22 | */ 23 | public void setId(Object id); 24 | 25 | /** 26 | * Get the name of the game. Preferably should be a unique name. 27 | * 28 | * @return Returns the name of the game. 29 | */ 30 | public String getGameName(); 31 | 32 | /** 33 | * Set the name of the game. Preferably it should be a unique value. 34 | * 35 | * @param gameName 36 | * Set the preferably unique game name. 37 | */ 38 | public void setGameName(String gameName); 39 | 40 | /** 41 | * Unloads the current game, by closing all sessions. 42 | * 43 | * @return In case of Netty Implementation it would return a collection of 44 | * {@link ChannelFuture} object. 45 | */ 46 | public Object unload(); 47 | } -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/handlers/netty/EventObjectEncoder.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.handlers.netty; 2 | 3 | import io.nadron.client.event.Event; 4 | import io.nadron.client.event.Events; 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.handler.codec.MessageToByteEncoder; 8 | import io.netty.handler.codec.serialization.ObjectEncoder; 9 | 10 | import java.io.Serializable; 11 | 12 | public class EventObjectEncoder extends MessageToByteEncoder{ 13 | 14 | @Override 15 | protected void encode(ChannelHandlerContext ctx, Event event, ByteBuf out) 16 | throws Exception { 17 | out.writeByte(event.getType()); 18 | if (Events.LOG_IN == event.getType() || Events.RECONNECT == event.getType()) 19 | { 20 | // write protocol version also 21 | out.writeByte(Events.PROTOCOL_VERSION); 22 | } 23 | 24 | if (null != event.getSource()) 25 | { 26 | new SourceEncoder().encode(ctx, (Serializable)event.getSource(), out); 27 | } 28 | } 29 | 30 | public static class SourceEncoder extends ObjectEncoder 31 | { 32 | @Override 33 | protected void encode(ChannelHandlerContext ctx, Serializable msg, 34 | ByteBuf out) throws Exception { 35 | super.encode(ctx, msg, out); 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/handlers/netty/MsgPackDecoder.java: -------------------------------------------------------------------------------- 1 | package io.nadron.handlers.netty; 2 | 3 | import io.nadron.event.Events; 4 | import io.nadron.util.NettyUtils; 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.channel.ChannelHandler.Sharable; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import io.netty.handler.codec.MessageToMessageDecoder; 9 | 10 | import java.util.List; 11 | 12 | import org.msgpack.MessagePack; 13 | import org.msgpack.type.Value; 14 | 15 | @Sharable 16 | public class MsgPackDecoder extends MessageToMessageDecoder 17 | { 18 | 19 | private MessagePack msgPack; 20 | 21 | @Override 22 | protected void decode(ChannelHandlerContext ctx, ByteBuf msg, 23 | List out) throws Exception 24 | { 25 | int opcode = msg.readUnsignedByte(); 26 | if (Events.LOG_IN == opcode || Events.RECONNECT == opcode) 27 | { 28 | msg.readUnsignedByte();// To read-destroy the protocol version byte. 29 | } 30 | 31 | Value source = msgPack.read(NettyUtils.toByteArray(msg, true)); 32 | out.add(Events.event(source, opcode)); 33 | } 34 | 35 | public MessagePack getMsgPack() 36 | { 37 | return msgPack; 38 | } 39 | 40 | public void setMsgPack(MessagePack msgPack) 41 | { 42 | this.msgPack = msgPack; 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/handlers/netty/MsgPackEncoder.java: -------------------------------------------------------------------------------- 1 | package io.nadron.handlers.netty; 2 | 3 | import java.util.List; 4 | 5 | import org.msgpack.MessagePack; 6 | 7 | import io.nadron.event.Event; 8 | import io.netty.buffer.ByteBuf; 9 | import io.netty.buffer.Unpooled; 10 | import io.netty.channel.ChannelHandlerContext; 11 | import io.netty.channel.ChannelHandler.Sharable; 12 | import io.netty.handler.codec.MessageToMessageEncoder;; 13 | 14 | @Sharable 15 | public class MsgPackEncoder extends MessageToMessageEncoder { 16 | 17 | MessagePack msgPack; 18 | 19 | @Override 20 | protected void encode(ChannelHandlerContext ctx, Event event, 21 | List out) throws Exception 22 | { 23 | ByteBuf msg = null; 24 | if(null != event.getSource()) 25 | { 26 | ByteBuf buf = ctx.alloc().buffer(1); 27 | buf.writeByte(event.getType()); 28 | msg = Unpooled.wrappedBuffer(buf, 29 | Unpooled.wrappedBuffer(msgPack.write(event.getSource()))); 30 | } 31 | else 32 | { 33 | msg = ctx.alloc().buffer(1); 34 | msg.writeByte(event.getType()); 35 | } 36 | out.add(msg); 37 | } 38 | 39 | public MessagePack getMsgPack() { 40 | return msgPack; 41 | } 42 | 43 | public void setMsgPack(MessagePack msgPack) { 44 | this.msgPack = msgPack; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /nadron/src/test/java/io/nadron/SpringNettyServer.java: -------------------------------------------------------------------------------- 1 | package io.nadron; 2 | 3 | import io.nadron.server.ServerManager; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; 8 | import org.springframework.context.support.AbstractApplicationContext; 9 | 10 | 11 | /** 12 | * This class starts the server running at the designated port on the localhost. 13 | * It defaults to port 8090 but can use any port specified at command prompt. 14 | * 15 | * @author Abraham Menacherry 16 | * 17 | */ 18 | public class SpringNettyServer 19 | { 20 | private static final Logger LOG = LoggerFactory.getLogger(SpringNettyServer.class); 21 | public static void main(String[] args) 22 | { 23 | // Log4j2 auto-configures from log4j2.xml in classpath 24 | AbstractApplicationContext context = new AnnotationConfigApplicationContext(NadronSpringConfig.class); 25 | // For the destroy method to work. 26 | context.registerShutdownHook(); 27 | 28 | // Start tcp and flash servers 29 | ServerManager manager = (ServerManager)context.getBean("serverManager"); 30 | try 31 | { 32 | manager.startServers(18090,843,18090); 33 | } 34 | catch (Exception e) 35 | { 36 | LOG.error("Could not start servers cleanly: {}",e); 37 | } 38 | } 39 | 40 | 41 | } 42 | 43 | -------------------------------------------------------------------------------- /example-games/src/main/java/io/nadron/example/zombie/domain/World.java: -------------------------------------------------------------------------------- 1 | package io.nadron.example.zombie.domain; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | 7 | public class World 8 | { 9 | private static final Logger LOG = LoggerFactory.getLogger(World.class); 10 | private volatile int alive; 11 | private volatile int undead; 12 | 13 | public boolean apocalypse() { 14 | if(alive <= 0) 15 | { 16 | return true; 17 | } 18 | return false; 19 | } 20 | 21 | public void report() { 22 | if(alive > 0) { 23 | LOG.trace("alive= {} undead= {}",alive, undead); 24 | } 25 | } 26 | 27 | public int getAlive() 28 | { 29 | return alive; 30 | } 31 | 32 | public void setAlive(int alive) 33 | { 34 | this.alive = alive; 35 | } 36 | 37 | public int getUndead() 38 | { 39 | return undead; 40 | } 41 | 42 | public void setUndead(int undead) 43 | { 44 | this.undead = undead; 45 | } 46 | 47 | public void shotgun() 48 | { 49 | int newUndead = undead - 1; 50 | LOG.trace("Defender update, undead = " + undead + " new undead: " + newUndead); 51 | undead = newUndead; 52 | } 53 | 54 | public void eatBrains() 55 | { 56 | LOG.trace("In eatBrains Alive: {} Undead: {}",alive,undead); 57 | alive--; 58 | undead += 2 ; 59 | LOG.trace("New Alive: {} Undead: {}",alive,undead); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /jetclient-as3/src/main/flex/io/nadron/util/LoginHelper.as: -------------------------------------------------------------------------------- 1 | package io.nadron.util 2 | { 3 | import io.nadron.communication.MessageBuffer; 4 | import io.nadron.event.NadEvent; 5 | import io.nadron.event.Events; 6 | import flash.utils.ByteArray; 7 | 8 | /** 9 | * ... 10 | * @author Abraham Menacherry 11 | */ 12 | public class LoginHelper 13 | { 14 | private var username:String; 15 | private var password:String; 16 | private var connectionKey:Object; 17 | private var remoteHost:String; 18 | private var remotePort:int; 19 | 20 | public function LoginHelper(username:String, password:String, connectionKey:Object, 21 | remoteHost:String,remotePort:int = 18090) 22 | { 23 | this.username = username; 24 | this.password = password; 25 | this.connectionKey = connectionKey; 26 | this.remoteHost = remoteHost; 27 | this.remotePort = remotePort; 28 | } 29 | 30 | public function getLoginEvent():NadEvent 31 | { 32 | var loginBuffer:MessageBuffer = new MessageBuffer(new ByteArray()); 33 | loginBuffer.writeMultiStrings(username, password, connectionKey); 34 | return Events.event(Events.LOG_IN, loginBuffer); 35 | } 36 | 37 | public function getRemoteHost():String 38 | { 39 | return remoteHost; 40 | } 41 | 42 | public function getRemotePort():int 43 | { 44 | return remotePort; 45 | } 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/handlers/netty/TextWebsocketEncoder.java: -------------------------------------------------------------------------------- 1 | package io.nadron.handlers.netty; 2 | 3 | import io.nadron.event.Event; 4 | import io.netty.channel.ChannelHandler.Sharable; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.handler.codec.MessageToMessageEncoder; 7 | import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; 8 | 9 | import java.util.List; 10 | 11 | import com.fasterxml.jackson.databind.ObjectMapper; 12 | 13 | /** 14 | * This encoder will convert an incoming object (mostly expected to be an 15 | * {@link Event} object) to a {@link TextWebSocketFrame} object. It uses 16 | * {@link ObjectMapper} from jackson library to do the Object to JSon String 17 | * encoding. 18 | * 19 | * @author Abraham Menacherry 20 | * 21 | */ 22 | @Sharable 23 | public class TextWebsocketEncoder extends MessageToMessageEncoder 24 | { 25 | 26 | private ObjectMapper jackson; 27 | 28 | @Override 29 | protected void encode(ChannelHandlerContext ctx, Event msg, 30 | List out) throws Exception 31 | { 32 | String json = jackson.writeValueAsString(msg); 33 | out.add(new TextWebSocketFrame(json)); 34 | } 35 | 36 | public ObjectMapper getJackson() 37 | { 38 | return jackson; 39 | } 40 | 41 | public void setJackson(ObjectMapper jackson) 42 | { 43 | this.jackson = jackson; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/protocol/impl/NettyObjectProtocol.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.protocol.impl; 2 | 3 | import io.nadron.client.app.Session; 4 | import io.nadron.client.handlers.netty.DefaultToClientHandler; 5 | import io.nadron.client.handlers.netty.EventObjectDecoder; 6 | import io.nadron.client.handlers.netty.EventObjectEncoder; 7 | import io.nadron.client.protocol.Protocol; 8 | import io.nadron.client.util.NettyUtils; 9 | import io.netty.channel.ChannelPipeline; 10 | import io.netty.handler.codec.LengthFieldBasedFrameDecoder; 11 | import io.netty.handler.codec.LengthFieldPrepender; 12 | 13 | public class NettyObjectProtocol implements Protocol{ 14 | 15 | public static final NettyObjectProtocol INSTANCE = new NettyObjectProtocol(); 16 | 17 | @Override 18 | public void applyProtocol(Session session) { 19 | ChannelPipeline pipeline = NettyUtils.getPipeLineOfSession(session); 20 | NettyUtils.clearPipeline(pipeline); 21 | pipeline.addLast("lengthDecoder", new LengthFieldBasedFrameDecoder( 22 | Integer.MAX_VALUE, 0, 2, 0, 2)); 23 | pipeline.addLast("eventDecoder", new EventObjectDecoder()); 24 | pipeline.addLast(new DefaultToClientHandler(session)); 25 | pipeline.addLast("lengthFieldPrepender", new LengthFieldPrepender( 26 | 2)); 27 | pipeline.addLast("eventEncoder", new EventObjectEncoder()); 28 | } 29 | 30 | 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /nadclient/src/test/java/io/nadron/client/util/LoginHelperTest.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.util; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import io.nadron.client.util.LoginHelper; 5 | import io.nadron.client.util.NettyUtils; 6 | import io.nadron.client.util.LoginHelper.LoginBuilder; 7 | import io.netty.buffer.ByteBuf; 8 | 9 | import java.net.InetSocketAddress; 10 | 11 | import org.junit.jupiter.api.Test; 12 | 13 | public class LoginHelperTest { 14 | 15 | @Test 16 | public void writeAndReadCredsTest() throws Exception 17 | { 18 | LoginBuilder builder = new LoginBuilder().username("user") 19 | .password("pass").connectionKey("Zombie_ROOM_1") 20 | .nadronTcpHostName("localhost").tcpPort(18090) 21 | .nadronUdpHostName("255.255.255.255").udpPort(18090); 22 | LoginHelper helper = builder.build(); 23 | ByteBuf byteBuf = helper.getLoginBuffer(new InetSocketAddress(18090)).getNativeBuffer(); 24 | assertEquals("user" , NettyUtils.readString(byteBuf)); 25 | assertEquals("pass" , NettyUtils.readString(byteBuf)); 26 | assertEquals("Zombie_ROOM_1" , NettyUtils.readString(byteBuf)); 27 | assertEquals(new InetSocketAddress(18090) , NettyUtils.readSocketAddress(byteBuf)); 28 | } 29 | 30 | @Test 31 | public void doManyLogins() throws Exception 32 | { 33 | for(int i = 1; i <1000; i++){ 34 | writeAndReadCredsTest(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/concurrent/JetlangActor.java: -------------------------------------------------------------------------------- 1 | package io.nadron.concurrent; 2 | 3 | import org.jetlang.channels.Channel; 4 | import org.jetlang.channels.MemoryChannel; 5 | import org.jetlang.core.Callback; 6 | import org.jetlang.fibers.Fiber; 7 | 8 | public class JetlangActor 9 | { 10 | private final Channel inChannel; 11 | private final Channel outChannel; 12 | private final Fiber fiber; 13 | private final Callback callback; 14 | 15 | public JetlangActor(Channel inChannel,Channel outChannel,Fiber fiber,Callback callback) 16 | { 17 | this.inChannel = inChannel; 18 | this.outChannel = outChannel; 19 | this.fiber = fiber; 20 | this.callback = callback; 21 | } 22 | 23 | public JetlangActor() 24 | { 25 | this.inChannel = new MemoryChannel(); 26 | this.outChannel = new MemoryChannel(); 27 | this.callback = new Callback(){ 28 | public void onMessage(T message) { 29 | act(message); 30 | }; 31 | }; 32 | this.fiber = Fibers.pooledFiber(); 33 | } 34 | 35 | public void start() 36 | { 37 | // subscribe to incoming channel 38 | inChannel.subscribe(fiber, callback); 39 | } 40 | 41 | public void act(T message) 42 | { 43 | 44 | } 45 | 46 | public void sendMessage(T message) 47 | { 48 | outChannel.publish(message); 49 | } 50 | 51 | public void addEventListener() 52 | { 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /nadron/src/test/java/io/nadron/util/SessionHandlerLatchCounter.java: -------------------------------------------------------------------------------- 1 | package io.nadron.util; 2 | 3 | import io.nadron.app.Session; 4 | import io.nadron.event.Event; 5 | import io.nadron.event.NetworkEvent; 6 | import io.nadron.event.impl.DefaultSessionEventHandler; 7 | 8 | import java.util.concurrent.CountDownLatch; 9 | import java.util.concurrent.atomic.AtomicLong; 10 | 11 | 12 | public class SessionHandlerLatchCounter extends DefaultSessionEventHandler { 13 | 14 | private final AtomicLong counter; 15 | private final CountDownLatch latch; 16 | 17 | public SessionHandlerLatchCounter(Session session, AtomicLong counter, 18 | CountDownLatch latch) { 19 | super(session); 20 | this.counter = counter; 21 | this.latch = latch; 22 | } 23 | 24 | @Override 25 | public void onNetworkMessage(NetworkEvent event) { 26 | counter.incrementAndGet(); 27 | latch.countDown(); 28 | System.out.println("invoked onNetworkMessage"); 29 | } 30 | 31 | @Override 32 | protected void onDisconnect(Event event) 33 | { 34 | counter.incrementAndGet(); 35 | latch.countDown(); 36 | System.out.println("invoked onDisconnect"); 37 | super.onDisconnect(event); 38 | } 39 | 40 | public AtomicLong getCounter() { 41 | return counter; 42 | } 43 | 44 | public CountDownLatch getLatch() { 45 | return latch; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/util/RandomStringGenerator.java: -------------------------------------------------------------------------------- 1 | package io.nadron.util; 2 | 3 | import java.security.NoSuchAlgorithmException; 4 | import java.security.SecureRandom; 5 | 6 | public class RandomStringGenerator 7 | { 8 | public static final int DEFAULT_LENGTH = 8; 9 | 10 | static char[] alphaNumberic = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 11 | 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 12 | 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 13 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 14 | 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', '3', '4', '5', '6', 15 | '7', '8', '9', '0' }; 16 | 17 | public static String generateRandomString(int length) 18 | { 19 | String random = "ACK"; 20 | 21 | int len = DEFAULT_LENGTH; 22 | if (length > 0) 23 | { 24 | len = length; 25 | } 26 | 27 | char[] randomChars = new char[len]; 28 | try 29 | { 30 | SecureRandom wheel = SecureRandom.getInstance("SHA1PRNG"); 31 | for (int i = 0; i < len; i++) 32 | { 33 | int nextChar = wheel.nextInt(alphaNumberic.length); 34 | randomChars[i] = alphaNumberic[nextChar]; 35 | } 36 | random = new String(randomChars); 37 | } 38 | catch (NoSuchAlgorithmException e) 39 | { 40 | // TODO Auto-generated catch block 41 | e.printStackTrace(); 42 | } 43 | 44 | return random; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /jetclient-as3/src/main/flex/io/nadron/codecs/impl/MessagBufferEventDecoder.as: -------------------------------------------------------------------------------- 1 | package io.nadron.codecs.impl 2 | { 3 | import flash.utils.ByteArray; 4 | import io.nadron.codecs.Transform; 5 | import io.nadron.event.Events; 6 | import io.nadron.event.NadEvent; 7 | import io.nadron.communication.MessageBuffer; 8 | 9 | /** 10 | * This decoder will convert an incoming ByteArray into a NadEvent. It will first read the opcode 11 | * byte to see which kind of event is coming from remote nadron server, say SESSION_MESSAGE and it will 12 | * read the payload. The opcode will be used to create appropriate event type and the payload will 13 | * be written to a MessageBuffer. 14 | * @author Abraham Menacherry 15 | */ 16 | public class MessagBufferEventDecoder implements Transform 17 | { 18 | 19 | public function MessagBufferEventDecoder() 20 | { 21 | 22 | } 23 | 24 | /* INTERFACE io.nadron.codecs.Transform */ 25 | 26 | public function transform(input:Object):Object 27 | { 28 | var message:ByteArray = input as ByteArray; 29 | 30 | var eventType:int = message.readByte(); 31 | if (eventType == Events.NETWORK_MESSAGE) 32 | { 33 | eventType = Events.SESSION_MESSAGE; 34 | } 35 | var buffer:MessageBuffer = new MessageBuffer(message); 36 | var event:NadEvent = Events.event(eventType, buffer); 37 | return event; 38 | } 39 | 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /jetclient-as3/src/main/flex/io/nadron/communication/DefaultMessageSender.as: -------------------------------------------------------------------------------- 1 | package io.nadron.communication 2 | { 3 | import flash.net.Socket; 4 | import flash.utils.ByteArray; 5 | import io.nadron.app.Session; 6 | import io.nadron.codecs.Transform; 7 | /** 8 | * A class used by the session to send messages to the remote Nadron Server. It contains methods for writing 9 | * messages as well as closing socket. It also has responsibility to tranform incoming object to bytes using 10 | * codec chain before writing to socket. 11 | * @author Abraham Menacherry 12 | */ 13 | public class DefaultMessageSender implements MessageSender 14 | { 15 | private var socket:Socket; 16 | private var transform:Transform; 17 | 18 | public function DefaultMessageSender(socket:Socket,transform:Transform) 19 | { 20 | this.socket = socket; 21 | this.transform = transform; 22 | } 23 | 24 | public function sendMessage(message:Object):void 25 | { 26 | if (null == message) { 27 | return; 28 | } 29 | var bytes:ByteArray = transform.transform(message) as ByteArray; 30 | socket.writeBytes(bytes); 31 | socket.flush(); 32 | } 33 | 34 | public function getSocket():Socket 35 | { 36 | return socket; 37 | } 38 | 39 | public function close():void 40 | { 41 | if (null != socket) 42 | { 43 | socket.close(); 44 | } 45 | } 46 | 47 | 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/communication/MessageSender.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.communication; 2 | 3 | 4 | /** 5 | * This interface declares method for sending a message to client. Different 6 | * implementations would be used by the server for sending based on the delivery 7 | * guaranty that is required. 8 | * 9 | * @author Abraham Menacherry 10 | * 11 | */ 12 | public interface MessageSender 13 | { 14 | /** 15 | * This method delegates to the underlying native session object to send a 16 | * message to the client. 17 | * 18 | * @param message 19 | * The message to be sent to client. 20 | * @return The boolean or future associated with this operation if 21 | * synchronous or asynchronous implementation respectively. 22 | */ 23 | Object sendMessage(Object message); 24 | 25 | /** 26 | * Returns the delivery guaranty of the implementation. Currently only 27 | * RELIABLE and FAST are supported, their respective integer values are 0 28 | * and 1. 29 | * 30 | * @return The guaranty instance associated with the implementation. 31 | */ 32 | DeliveryGuaranty getDeliveryGuaranty(); 33 | 34 | /** 35 | * Cleanup hook which can be called when a session is disconnected or 36 | * closed. 37 | */ 38 | void close(); 39 | 40 | public interface Reliable extends MessageSender{} 41 | 42 | public interface Fast extends MessageSender{} 43 | } 44 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/handlers/netty/EventEncoder.java: -------------------------------------------------------------------------------- 1 | package io.nadron.handlers.netty; 2 | 3 | import io.nadron.event.Event; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.buffer.Unpooled; 6 | import io.netty.channel.ChannelHandler.Sharable; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import io.netty.handler.codec.MessageToMessageEncoder; 9 | 10 | import java.util.List; 11 | 12 | 13 | 14 | /** 15 | * A simple event encoder will receive an incoming event, and convert it to a 16 | * {@link ByteBuf}. It will read the event type and put it as the 17 | * opcode(i.e first byte of the buffer), then it will read the event body and 18 | * put convert to ChannelBuffer if necessary and put it as the body of the 19 | * message. 20 | * 21 | * @author Abraham Menacherry 22 | * 23 | */ 24 | @Sharable 25 | public class EventEncoder extends MessageToMessageEncoder 26 | { 27 | 28 | @Override 29 | protected void encode(ChannelHandlerContext ctx, Event event, 30 | List out) throws Exception 31 | { 32 | ByteBuf opcode = ctx.alloc().buffer(1); 33 | opcode.writeByte(event.getType()); 34 | if(null != event.getSource()) 35 | { 36 | ByteBuf data = (ByteBuf) event.getSource(); 37 | ByteBuf compositeBuffer = Unpooled.wrappedBuffer(opcode, data); 38 | out.add(compositeBuffer); 39 | } 40 | else 41 | { 42 | out.add(opcode); 43 | } 44 | 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/concurrent/Lanes.java: -------------------------------------------------------------------------------- 1 | package io.nadron.concurrent; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | import java.util.concurrent.ThreadFactory; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | public enum Lanes 10 | { 11 | LANES; 12 | final String serverCores = System.getProperty("jet.lanes"); 13 | final int numOfCores; 14 | final Lane[] jetLanes; 15 | 16 | @SuppressWarnings("unchecked") 17 | Lanes() 18 | { 19 | final Logger LOG = LoggerFactory.getLogger(Lanes.class); 20 | 21 | int cores = 1; 22 | if (null != serverCores) 23 | { 24 | try 25 | { 26 | cores = Integer.parseInt(serverCores); 27 | } 28 | catch (NumberFormatException e) 29 | { 30 | LOG.warn("Invalid server cores {} passed in, going to ignore",serverCores); 31 | // ignore; 32 | } 33 | } 34 | numOfCores = cores; 35 | jetLanes = new Lane[cores]; 36 | ThreadFactory threadFactory = new NamedThreadFactory("Lane",true); 37 | for (int i = 1; i <= cores; i++) 38 | { 39 | DefaultLane defaultLane = new DefaultLane("Lane[" + i + "]", 40 | ManagedExecutor.newSingleThreadExecutor(threadFactory)); 41 | jetLanes[i - 1] = defaultLane; 42 | } 43 | } 44 | 45 | public Lane[] getJetLanes() 46 | { 47 | return jetLanes; 48 | } 49 | 50 | public int getNumOfCores() 51 | { 52 | return numOfCores; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/service/LookupService.java: -------------------------------------------------------------------------------- 1 | package io.nadron.service; 2 | 3 | import io.nadron.app.Game; 4 | import io.nadron.app.GameRoom; 5 | import io.nadron.app.Player; 6 | import io.nadron.util.Credentials; 7 | 8 | 9 | public interface LookupService 10 | { 11 | /** 12 | * Get a game room based on a reference key. This allows the connection 13 | * service to connect to a game room of a game. 14 | * 15 | * @param gameContextKey 16 | * This is the key the flex front end passes to handshake 17 | * service. 18 | * @return Returns the game room associated with this key or null if no such 19 | * game room exists. 20 | */ 21 | public abstract GameRoom gameRoomLookup(Object gameContextKey); 22 | 23 | /** 24 | * Get a game based on a reference key. This allows the player session to 25 | * get its associated game. 26 | * 27 | * @param gameContextKey 28 | * @return Returns the game instance based on the key provided. Null if 29 | * invalid key is provided. 30 | */ 31 | public abstract Game gameLookup(Object gameContextKey); 32 | 33 | /** 34 | * Lookup a gamer based on a context key. 35 | * 36 | * @param loginDetail Contains the username and password. 37 | * @return Returns the gamer instance based on the key provided. Null if 38 | * invalid key is provided. 39 | */ 40 | public abstract Player playerLookup(Credentials loginDetail); 41 | 42 | } 43 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/concurrent/DefaultLane.java: -------------------------------------------------------------------------------- 1 | package io.nadron.concurrent; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | 5 | public class DefaultLane implements Lane 6 | { 7 | 8 | final String laneName; 9 | final ExecutorService exec; 10 | 11 | public DefaultLane(String threadName, ExecutorService exec) 12 | { 13 | this.laneName = threadName; 14 | this.exec = exec; 15 | } 16 | 17 | @Override 18 | public boolean isOnSameLane(String currentThread) 19 | { 20 | return currentThread.equals(laneName); 21 | } 22 | 23 | @Override 24 | public ExecutorService getUnderlyingLane() 25 | { 26 | return exec; 27 | } 28 | 29 | @Override 30 | public int hashCode() 31 | { 32 | final int prime = 31; 33 | int result = 1; 34 | result = prime * result 35 | + ((laneName == null) ? 0 : laneName.hashCode()); 36 | return result; 37 | } 38 | 39 | @Override 40 | public boolean equals(Object obj) 41 | { 42 | if (this == obj) 43 | return true; 44 | if (obj == null) 45 | return false; 46 | if (getClass() != obj.getClass()) 47 | return false; 48 | DefaultLane other = (DefaultLane) obj; 49 | if (laneName == null) 50 | { 51 | if (other.laneName != null) 52 | return false; 53 | } 54 | else if (!laneName.equals(other.laneName)) 55 | return false; 56 | return true; 57 | } 58 | 59 | @Override 60 | public String getId() 61 | { 62 | return laneName; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/handlers/netty/MessageBufferEventDecoder.java: -------------------------------------------------------------------------------- 1 | package io.nadron.handlers.netty; 2 | 3 | import io.nadron.communication.NettyMessageBuffer; 4 | import io.nadron.event.Event; 5 | import io.nadron.event.Events; 6 | import io.netty.buffer.ByteBuf; 7 | import io.netty.channel.ChannelHandler.Sharable; 8 | import io.netty.channel.ChannelHandlerContext; 9 | import io.netty.handler.codec.MessageToMessageDecoder; 10 | 11 | import java.util.List; 12 | 13 | 14 | /** 15 | * This decoder will convert a Netty {@link ByteBuf} to a 16 | * {@link NettyMessageBuffer}. It will also convert 17 | * {@link Events#NETWORK_MESSAGE} events to {@link Events#SESSION_MESSAGE} 18 | * event. 19 | * 20 | * @author Abraham Menacherry 21 | * 22 | */ 23 | //TODO check if MessageToMessageDecoder can be replaced with MessageToByteDecoder 24 | @Sharable 25 | public class MessageBufferEventDecoder extends MessageToMessageDecoder 26 | { 27 | 28 | @Override 29 | protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, 30 | List out) throws Exception 31 | { 32 | out.add(decode(ctx, buffer)); 33 | } 34 | 35 | public Event decode(ChannelHandlerContext ctx, ByteBuf in){ 36 | byte opcode = in.readByte(); 37 | if (opcode == Events.NETWORK_MESSAGE) 38 | { 39 | opcode = Events.SESSION_MESSAGE; 40 | } 41 | ByteBuf data = in.readBytes(in.readableBytes()); 42 | return Events.event(new NettyMessageBuffer(data), opcode); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/handlers/netty/EventObjectDecoder.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.handlers.netty; 2 | 3 | import io.nadron.client.event.Events; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.handler.codec.MessageToMessageDecoder; 7 | import io.netty.handler.codec.serialization.ClassResolvers; 8 | import io.netty.handler.codec.serialization.ObjectDecoder; 9 | 10 | import java.util.List; 11 | 12 | public class EventObjectDecoder extends MessageToMessageDecoder 13 | { 14 | 15 | @Override 16 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, 17 | List out) throws Exception 18 | { 19 | if(null != in) 20 | { 21 | byte opcode = in.readByte(); 22 | if (opcode == Events.NETWORK_MESSAGE) 23 | { 24 | opcode = Events.SESSION_MESSAGE; 25 | } 26 | ByteBuf data = in.readBytes(in.readableBytes()); 27 | // TODO check if creating a new object is necessary each time 28 | Object obj = new SourceDecoder().decode(ctx, data); 29 | out.add(Events.event(obj, opcode)); 30 | } 31 | } 32 | 33 | public static class SourceDecoder extends ObjectDecoder 34 | { 35 | public SourceDecoder() 36 | { 37 | super(ClassResolvers.cacheDisabled(null)); 38 | } 39 | 40 | @Override 41 | protected Object decode(ChannelHandlerContext ctx, ByteBuf in) 42 | throws Exception 43 | { 44 | return super.decode(ctx, in); 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/service/UniqueIDGeneratorService.java: -------------------------------------------------------------------------------- 1 | package io.nadron.service; 2 | 3 | /** 4 | * This class is used to generate unique id's across the network. For a single 5 | * VM the default implementation would be and incrementing long counter. 6 | * 7 | * @author Abraham.Menacherry 8 | * 9 | */ 10 | public interface UniqueIDGeneratorService 11 | { 12 | /** 13 | * Returns a unique id across the network, including those generated by 14 | * {@link #generateFor(Class)} method. For e.g. if generateFor has already 15 | * generated id "1", this method will not return "1" anymore. For a single 16 | * JVM server, this method would just send back an Atomically incremented 17 | * counter. 18 | * 19 | * @return A unique id. 20 | */ 21 | public Object generate(); 22 | 23 | /** 24 | * Returns a unique id across the network, including those generated by 25 | * {@link #generate()} method. For e.g. if generate has already generated id 26 | * "1", this method will not return "1" anymore. For a single JVM server, 27 | * this method would just send back an Atomically incremented counter. 28 | * 29 | * @param klass 30 | * The class for which this method is being generated. The 31 | * generator implementation can then probably put in a suffix of 32 | * identification for generated id's. 33 | * @return A unique id. 34 | */ 35 | public Object generateFor(@SuppressWarnings("rawtypes") Class klass); 36 | } 37 | -------------------------------------------------------------------------------- /example-games/src/test/java/io/nadron/example/zombieclient/GamePlay.java: -------------------------------------------------------------------------------- 1 | package io.nadron.example.zombieclient; 2 | 3 | import io.nadron.client.app.Session; 4 | import io.nadron.client.communication.MessageBuffer; 5 | import io.nadron.client.communication.NettyMessageBuffer; 6 | import io.nadron.client.communication.DeliveryGuaranty.DeliveryGuarantyOptions; 7 | import io.nadron.client.event.Event; 8 | import io.nadron.client.event.Events; 9 | import io.nadron.example.zombie.domain.IAM; 10 | import io.nadron.example.zombie.domain.ZombieCommands; 11 | import io.netty.buffer.ByteBuf; 12 | 13 | 14 | public class GamePlay implements Runnable 15 | { 16 | private final IAM iam; 17 | private final Session session; 18 | 19 | public GamePlay(IAM iam, Session session) 20 | { 21 | this.iam = iam; 22 | this.session = session; 23 | } 24 | 25 | @Override 26 | public void run() 27 | { 28 | int type = IAM.getInt(iam); 29 | int operation = 0; 30 | switch(iam) 31 | { 32 | case DEFENDER: 33 | operation = ZombieCommands.SHOT_GUN.getCommand(); 34 | break; 35 | case ZOMBIE: 36 | operation = ZombieCommands.EAT_BRAINS.getCommand(); 37 | break; 38 | } 39 | 40 | for(int i = 1; i<10; i++){ 41 | MessageBuffer messageBuffer = new NettyMessageBuffer(); 42 | messageBuffer.writeInt(type); 43 | messageBuffer.writeInt(operation); 44 | Event event = Events.networkEvent(messageBuffer, DeliveryGuarantyOptions.FAST); 45 | session.onEvent(event); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/handlers/netty/MessageBufferEventDecoder.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.handlers.netty; 2 | 3 | import io.nadron.client.communication.NettyMessageBuffer; 4 | import io.nadron.client.event.Event; 5 | import io.nadron.client.event.Events; 6 | import io.netty.buffer.ByteBuf; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import io.netty.handler.codec.ByteToMessageDecoder; 9 | 10 | import java.util.List; 11 | 12 | 13 | /** 14 | * This decoder will convert a Netty {@link ByteBuf} to a 15 | * {@link NettyMessageBuffer}. It will also convert 16 | * {@link Events#NETWORK_MESSAGE} events to {@link Events#SESSION_MESSAGE} 17 | * event. 18 | * 19 | * @author Abraham Menacherry 20 | * 21 | */ 22 | public class MessageBufferEventDecoder extends ByteToMessageDecoder 23 | { 24 | @Override 25 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, 26 | List out) throws Exception 27 | { 28 | if (in.readableBytes() > 0) 29 | { 30 | out.add(decode(ctx, in)); 31 | } 32 | } 33 | 34 | protected Event decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception 35 | { 36 | if (in.readableBytes() > 0) 37 | { 38 | byte opcode = in.readByte(); 39 | if (opcode == Events.NETWORK_MESSAGE) 40 | { 41 | opcode = Events.SESSION_MESSAGE; 42 | } 43 | ByteBuf data = in.readBytes(in.readableBytes()); 44 | return Events.event(new NettyMessageBuffer(data), opcode); 45 | } 46 | return null; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/server/netty/NettyServer.java: -------------------------------------------------------------------------------- 1 | package io.nadron.server.netty; 2 | 3 | import io.nadron.server.Server; 4 | import io.netty.channel.Channel; 5 | import io.netty.channel.ChannelInitializer; 6 | 7 | 8 | /** 9 | * An interface specific to the JBoss Netty implementation. It will be 10 | * implemented by a class that will start up a Netty server at a specified port. 11 | * 12 | * @author Abraham Menacherry 13 | * 14 | */ 15 | public interface NettyServer extends Server 16 | { 17 | /** 18 | * createServerBootstrap will create a pipeline factory and save it as a 19 | * class variable. This method can then be used to retrieve that value. 20 | * 21 | * @return Returns the channel pipeline factory that is associated with this 22 | * netty server. 23 | */ 24 | public ChannelInitializer getChannelInitializer(); 25 | 26 | /** 27 | * Method can be used to set the pipeline factory that is to be used by the 28 | * netty server. 29 | * 30 | * @param initializer 31 | * The factory which will create a pipeline on each incoming 32 | * connection. 33 | */ 34 | public void setChannelInitializer(ChannelInitializer initializer); 35 | 36 | /** 37 | * Get the netty configuration associated with this server. 38 | * 39 | * @return returns the configuration instance which is passed in via 40 | * constructor. 41 | */ 42 | public NettyConfig getNettyConfig(); 43 | 44 | } 45 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/handlers/netty/EventObjectDecoder.java: -------------------------------------------------------------------------------- 1 | package io.nadron.handlers.netty; 2 | 3 | import io.nadron.event.Events; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.buffer.Unpooled; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.handler.codec.MessageToMessageDecoder; 8 | import io.netty.handler.codec.serialization.ClassResolvers; 9 | import io.netty.handler.codec.serialization.ObjectDecoder; 10 | 11 | import java.util.List; 12 | 13 | public class EventObjectDecoder extends MessageToMessageDecoder 14 | { 15 | 16 | @Override 17 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, 18 | List out) throws Exception { 19 | if(null != in) 20 | { 21 | byte opcode = in.readByte(); 22 | if (opcode == Events.NETWORK_MESSAGE) 23 | { 24 | opcode = Events.SESSION_MESSAGE; 25 | } 26 | ByteBuf data = Unpooled.buffer(in.readableBytes()).writeBytes(in); 27 | // TODO check if creating a new object is necessary each time 28 | Object obj = new SourceDecoder().decode(ctx, data); 29 | out.add(Events.event(obj, opcode)); 30 | } 31 | } 32 | 33 | public static class SourceDecoder extends ObjectDecoder 34 | { 35 | public SourceDecoder() 36 | { 37 | super(ClassResolvers.cacheDisabled(null)); 38 | } 39 | 40 | @Override 41 | protected Object decode(ChannelHandlerContext ctx, ByteBuf in) 42 | throws Exception 43 | { 44 | return super.decode(ctx, in); 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/server/netty/FlashPolicyServerChannelInitalizer.java: -------------------------------------------------------------------------------- 1 | package io.nadron.server.netty; 2 | 3 | import io.nadron.handlers.netty.FlashPolicyServerDecoder; 4 | import io.nadron.handlers.netty.FlashPolicyServerHandler; 5 | import io.netty.channel.ChannelInitializer; 6 | import io.netty.channel.ChannelPipeline; 7 | import io.netty.channel.socket.SocketChannel; 8 | import io.netty.handler.timeout.IdleStateHandler; 9 | 10 | 11 | 12 | /** 13 | * @author Bruce Mitchener 14 | */ 15 | public class FlashPolicyServerChannelInitalizer extends ChannelInitializer 16 | { 17 | 18 | // TODO make this configurable from spring. 19 | private static final int MAX_IDLE_SECONDS = 60; 20 | 21 | /** 22 | * Spring will return the actual prototype bean from its context here. It 23 | * uses method lookup here. 24 | * 25 | * @return a new instance of the {@link FlashPolicyServerHandler} 26 | */ 27 | protected FlashPolicyServerHandler getFlashPolicyServerHandler() 28 | { 29 | return null; 30 | } 31 | 32 | @Override 33 | protected void initChannel(SocketChannel ch) throws Exception { 34 | ChannelPipeline pipeline = ch.pipeline(); 35 | pipeline.addLast("idleStateCheck", new IdleStateHandler( 36 | MAX_IDLE_SECONDS, MAX_IDLE_SECONDS, MAX_IDLE_SECONDS)); 37 | pipeline.addLast("decoder", new FlashPolicyServerDecoder()); 38 | pipeline.addLast("handler", getFlashPolicyServerHandler()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/event/impl/DefaultEvent.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.event.impl; 2 | 3 | import io.nadron.client.event.Event; 4 | 5 | import java.io.Serializable; 6 | 7 | 8 | /** 9 | * Instances of this class are used to hold events that come in from remote 10 | * nadron server, for communicating between sessions and also for transmitting 11 | * messages to the remote nadron server from client. 12 | * 13 | * @author Abraham Menacherry 14 | * 15 | */ 16 | public class DefaultEvent implements Event, Serializable 17 | { 18 | /** 19 | * 20 | */ 21 | private static final long serialVersionUID = -1114679476675012101L; 22 | 23 | protected int type; 24 | protected Object source; 25 | protected long timeStamp; 26 | 27 | @Override 28 | public int getType() 29 | { 30 | return type; 31 | } 32 | 33 | @Override 34 | public Object getSource() 35 | { 36 | return source; 37 | } 38 | 39 | @Override 40 | public long getTimeStamp() 41 | { 42 | return timeStamp; 43 | } 44 | 45 | @Override 46 | public void setType(int type) 47 | { 48 | this.type = type; 49 | } 50 | 51 | @Override 52 | public void setSource(Object source) 53 | { 54 | this.source = source; 55 | } 56 | 57 | @Override 58 | public void setTimeStamp(long timeStamp) 59 | { 60 | this.timeStamp = timeStamp; 61 | 62 | } 63 | 64 | @Override 65 | public String toString() 66 | { 67 | return "Event [type=" + type + ", source=" + source + ", timeStamp=" 68 | + timeStamp + "]"; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /jetclient-as3/src/main/flex/io/nadron/codecs/impl/LoginInOutCodecs.as: -------------------------------------------------------------------------------- 1 | package io.nadron.codecs.impl 2 | { 3 | import io.nadron.codecs.CodecChain; 4 | import io.nadron.codecs.impl.DefaultCodecChain; 5 | import io.nadron.codecs.InAndOutCodecChain; 6 | 7 | /** 8 | * The logging in process requires both writing to remote jetserver as well as reading from it. 9 | * This set of in and out codecs will take care of transforming the events to be written and read 10 | * from remote server. 11 | * @author Abraham Menacherry 12 | */ 13 | public class LoginInOutCodecs implements InAndOutCodecChain 14 | { 15 | private var inCodecs:CodecChain; 16 | private var outCodecs:CodecChain; 17 | 18 | public function LoginInOutCodecs() 19 | { 20 | this.inCodecs = new DefaultCodecChain(); 21 | this.outCodecs = new DefaultCodecChain(); 22 | outCodecs.add(new MessageBufferEventEncoder()); 23 | outCodecs.add(new LengthFieldPrepender()); 24 | inCodecs.add(new LengthFieldBasedFrameDecoder()); 25 | inCodecs.add(new MessagBufferEventDecoder()); 26 | } 27 | 28 | public function getInCodecs():CodecChain 29 | { 30 | return inCodecs; 31 | } 32 | 33 | public function setInCodecs(inCodecs:CodecChain):void 34 | { 35 | this.inCodecs = inCodecs; 36 | } 37 | 38 | public function getOutCodecs():CodecChain 39 | { 40 | return outCodecs; 41 | } 42 | 43 | public function setOutCodecs(outCodecs:CodecChain):void 44 | { 45 | this.outCodecs = outCodecs; 46 | } 47 | 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/event/impl/NetworkEventListener.java: -------------------------------------------------------------------------------- 1 | package io.nadron.event.impl; 2 | 3 | import io.nadron.app.GameRoom; 4 | import io.nadron.app.Session; 5 | import io.nadron.event.Event; 6 | import io.nadron.event.Events; 7 | import io.nadron.event.NetworkEvent; 8 | import io.nadron.event.SessionEventHandler; 9 | 10 | /** 11 | * A listener class which will be used by {@link GameRoom} to send 12 | * {@link NetworkEvent}s to the connected sessions. When the game room 13 | * publishes such events to its channel, this listener will pick it up and 14 | * transmit it to the session which in turn will transmit it to the remote 15 | * machine/vm. 16 | * 17 | * @author Abraham Menacherry 18 | * 19 | */ 20 | public class NetworkEventListener implements SessionEventHandler 21 | { 22 | 23 | private static final int EVENT_TYPE = Events.NETWORK_MESSAGE; 24 | private final Session session; 25 | 26 | public NetworkEventListener(Session session) 27 | { 28 | this.session = session; 29 | } 30 | 31 | @Override 32 | public void onEvent(Event event) 33 | { 34 | session.onEvent(event); 35 | } 36 | 37 | @Override 38 | public int getEventType() 39 | { 40 | return EVENT_TYPE; 41 | } 42 | 43 | @Override 44 | public Session getSession() 45 | { 46 | return session; 47 | } 48 | 49 | @Override 50 | public void setSession(Session session) 51 | { 52 | throw new UnsupportedOperationException( 53 | "Session is a final field in this class. " 54 | + "It cannot be reset"); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /nadclient/src/test/java/io/nadron/client/UDPClientTest.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import io.nadron.client.handlers.netty.UDPPipelineFactory; 5 | import io.netty.bootstrap.Bootstrap; 6 | import io.netty.channel.ChannelOption; 7 | import io.netty.channel.group.DefaultChannelGroup; 8 | import io.netty.channel.nio.NioEventLoopGroup; 9 | import io.netty.channel.socket.DatagramChannel; 10 | import io.netty.channel.socket.nio.NioDatagramChannel; 11 | import io.netty.util.concurrent.GlobalEventExecutor; 12 | 13 | import java.net.InetSocketAddress; 14 | 15 | import org.junit.jupiter.api.Test; 16 | 17 | public class UDPClientTest { 18 | 19 | @Test 20 | public void multiChannelCreationTest() { 21 | NioEventLoopGroup boss = new NioEventLoopGroup(); 22 | DefaultChannelGroup defaultChannelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); 23 | for (int i = 0; i < 100; i++) { 24 | Bootstrap udpBootstrap = new Bootstrap(); 25 | udpBootstrap 26 | .group(boss) 27 | .channel(NioDatagramChannel.class) 28 | .option(ChannelOption.SO_BROADCAST, true) 29 | .handler( 30 | UDPPipelineFactory 31 | .getInstance(new InetSocketAddress(18090))); 32 | DatagramChannel datagramChannel = (DatagramChannel) udpBootstrap 33 | .bind(new InetSocketAddress(0)).syncUninterruptibly() 34 | .channel(); 35 | defaultChannelGroup.add(datagramChannel); 36 | } 37 | assertEquals(defaultChannelGroup.size(), 100); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/concurrent/Agent.java: -------------------------------------------------------------------------------- 1 | package io.nadron.concurrent; 2 | 3 | import org.jetlang.channels.Channel; 4 | import org.jetlang.channels.MemoryChannel; 5 | import org.jetlang.core.Callback; 6 | import org.jetlang.fibers.Fiber; 7 | 8 | /** 9 | * This class is used by the AppManaged aspect to transform a normal method call 10 | * into an asynchronous one. This class thus behaves like a Clojure or GPars 11 | * Agent. 12 | * 13 | * @author Abraham Menacherry 14 | * 15 | */ 16 | public class Agent 17 | { 18 | /** 19 | * The dedicated in-vm memory channel for this agent. Calls to this agent 20 | * get queued up on this channel for execution by the thread. 21 | */ 22 | final Channel channel; 23 | /** 24 | * The fiber associated with this agent. Used to subscribe to the channel 25 | * and pass the incoming code to the callback for execution. 26 | */ 27 | final Fiber fiber; 28 | /** 29 | * The incoming code is executed by this call back synchronously. Since the 30 | * send itself is asynchronous it acts like an event handler. 31 | */ 32 | final Callback callback = new Callback() 33 | { 34 | @Override 35 | public void onMessage(Runnable message) 36 | { 37 | message.run(); 38 | } 39 | }; 40 | 41 | public Agent() 42 | { 43 | this.channel = new MemoryChannel(); 44 | this.fiber = Fibers.pooledFiber(); 45 | channel.subscribe(fiber, callback); 46 | } 47 | 48 | public void send(Runnable code) 49 | { 50 | channel.publish(code); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/handlers/netty/UDPUpstreamHandler.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.handlers.netty; 2 | 3 | import io.nadron.client.NettyUDPClient; 4 | import io.nadron.client.app.Session; 5 | import io.nadron.client.event.Event; 6 | import io.netty.channel.ChannelHandler.Sharable; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import io.netty.channel.SimpleChannelInboundHandler; 9 | import io.netty.channel.socket.DatagramPacket; 10 | 11 | 12 | /** 13 | * This upstream handler handles ALL UDP events. It will lookup the 14 | * appropriate session from {@link NettyUDPClient#CLIENTS} map and then transmit 15 | * the event to that {@link Session}. Note If this class cannot find the 16 | * appropriate session to transmit this event to, then the event is 17 | * silently discarded. 18 | * 19 | * @author Abraham Menacherry. 20 | * 21 | */ 22 | @Sharable 23 | public class UDPUpstreamHandler extends SimpleChannelInboundHandler 24 | { 25 | private final MessageBufferEventDecoder decoder; 26 | 27 | public UDPUpstreamHandler() 28 | { 29 | super(); 30 | decoder = new MessageBufferEventDecoder(); 31 | } 32 | 33 | @Override 34 | public void channelRead0(ChannelHandlerContext ctx, 35 | DatagramPacket packet) throws Exception 36 | { 37 | Session session = NettyUDPClient.CLIENTS.get(ctx.channel().localAddress()); 38 | if (null != session) 39 | { 40 | Event event = (Event)decoder.decode(null, packet.content()); 41 | // Pass the event on to the session 42 | session.onEvent(event); 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /example-games/src/test/java/io/nadron/example/zombie/TimerCanceller.java: -------------------------------------------------------------------------------- 1 | package io.nadron.example.zombie; 2 | 3 | import io.nadron.event.Event; 4 | import io.nadron.event.Events; 5 | import io.nadron.example.zombie.domain.ZombieCommands; 6 | import io.netty.buffer.ByteBuf; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import io.netty.channel.SimpleChannelInboundHandler; 9 | 10 | import java.util.concurrent.ScheduledExecutorService; 11 | 12 | 13 | 14 | public class TimerCanceller extends SimpleChannelInboundHandler 15 | { 16 | String type = null; 17 | ScheduledExecutorService service = null; 18 | public TimerCanceller(String type,ScheduledExecutorService service) 19 | { 20 | this.type = type; 21 | this.service = service; 22 | } 23 | 24 | @Override 25 | public void channelRead0(ChannelHandlerContext ctx, 26 | Event event) throws Exception 27 | { 28 | if(Events.NETWORK_MESSAGE == event.getType()) 29 | { 30 | ByteBuf apocalypse = (ByteBuf) event.getSource(); 31 | if(apocalypse.readableBytes()>=4) 32 | { 33 | int cmd = apocalypse.readInt(); 34 | ZombieCommands command = ZombieCommands.CommandsEnum.fromInt(cmd); 35 | if(command == ZombieCommands.APOCALYPSE) 36 | { 37 | System.out.println("Cancelling " + type + " timer due to apocalypse"); 38 | service.shutdown(); 39 | ctx.channel().close(); 40 | } 41 | } 42 | } 43 | } 44 | 45 | @Override 46 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) 47 | throws Exception { 48 | cause.printStackTrace(); 49 | ctx.channel().close(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /nadclient-js/README.md: -------------------------------------------------------------------------------- 1 | This is a **Javascript** client project for [Nadron](https://github.com/menacher/java-game-server/tree/netty4/nadron) library. An example html which can connect to a locally running Nadron is located at **test/nadclient.html**. 2 | 3 | About the Client 4 | ================ 5 | Click on `Start War!` button after loading jetclient.html in a websocket compatible browser to start the game. Assumption is that Nadron server is running and nadclient.html is using accurate hostname and port number. 6 | 7 | Usage as your own game client 8 | ============================= 9 | The general usage steps could be as outlined below. 10 | 1. Create a `config` object containing the `user`, `password` and `connectionkey` to connect to game room. If you are using a different protocol, then add the appropriate `CodeChain`'s also to the `config` object. By default `JSon` encoding/decoding is used. 11 | 2. Create a `session`(s) using the `SessionFactory` by passing in a `url` for the remote Nadron server, `config` object and a `callback` function which will receive the `session` object after successful login to remote Nadron server. 12 | 3. Add necessary handlers to the session using `addHandler` function. The default events are provided the `nad` class for e.g `nad.lOG_IN`. At the very least you would want to add a handler for the `nad.SESSION_MESSAGE` event to receive incoming events from jetserver. 13 | 4. Event objects to be sent to server can be created using the `jet.nevent` function. 14 | 5. Data can be send to remote server using `session.send` function. 15 | 16 | Happy Coding!! 17 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/handlers/netty/JavaObjectToAMF3Encoder.java: -------------------------------------------------------------------------------- 1 | package io.nadron.handlers.netty; 2 | 3 | import io.nadron.convert.flex.AMFSerializer; 4 | import io.nadron.convert.flex.SerializationContextProvider; 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.channel.ChannelHandler.Sharable; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import io.netty.handler.codec.MessageToByteEncoder; 9 | 10 | import java.io.ByteArrayOutputStream; 11 | import java.io.IOException; 12 | 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | 17 | /** 18 | * This class will convert the incoming java object to Flex AMF3 byte format and 19 | * put them in a Netty {@link ByteBuf}. It will return this ChannelBuffer 20 | * to downstream handler. 21 | * 22 | * @author Abraham Menacherry 23 | * 24 | */ 25 | @Sharable 26 | public class JavaObjectToAMF3Encoder extends MessageToByteEncoder 27 | { 28 | private static final Logger LOG = LoggerFactory.getLogger(JavaObjectToAMF3Encoder.class); 29 | 30 | @Override 31 | protected void encode(ChannelHandlerContext ctx, 32 | Object msg, ByteBuf out) throws Exception { 33 | SerializationContextProvider contextProvider = new SerializationContextProvider(); 34 | AMFSerializer serializer = new AMFSerializer(contextProvider.get()); 35 | ByteArrayOutputStream baos = null; 36 | try 37 | { 38 | baos = serializer.toAmf(msg); 39 | out.writeBytes(baos.toByteArray()); 40 | baos.close(); 41 | } 42 | catch (IOException e) 43 | { 44 | LOG.error("IO Error: {}",e); 45 | throw e; 46 | } 47 | 48 | } 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /nadclient/src/test/java/io/nadron/TestClass.java: -------------------------------------------------------------------------------- 1 | package io.nadron; 2 | 3 | import io.nadron.client.app.Session; 4 | import io.nadron.client.app.impl.SessionFactory; 5 | import io.nadron.client.event.Event; 6 | import io.nadron.client.event.impl.AbstractSessionEventHandler; 7 | import io.nadron.client.util.LoginHelper; 8 | import io.nadron.client.util.LoginHelper.LoginBuilder; 9 | 10 | import java.net.UnknownHostException; 11 | 12 | 13 | /** 14 | * A simple test class for connecting Nad client to a remote nadron server. This does 15 | * not have any game logic and will just print out events coming from the 16 | * server. 17 | * 18 | * @author Abraham Menacherry 19 | * 20 | */ 21 | public class TestClass 22 | { 23 | 24 | /** 25 | * @param args 26 | * @throws Exception 27 | * @throws UnknownHostException 28 | */ 29 | public static void main(String[] args) throws UnknownHostException, 30 | Exception 31 | { 32 | LoginBuilder builder = new LoginBuilder().username("user") 33 | .password("pass").connectionKey("Zombie_ROOM_1_REF_KEY_1") 34 | .nadronTcpHostName("localhost").tcpPort(18090) 35 | .nadronUdpHostName("255.255.255.255").udpPort(18090); 36 | LoginHelper loginHelper = builder.build(); 37 | SessionFactory sessionFactory = new SessionFactory(loginHelper); 38 | Session session = sessionFactory.createAndConnectSession(); 39 | AbstractSessionEventHandler handler = new AbstractSessionEventHandler( 40 | session) 41 | { 42 | @Override 43 | public void onDataIn(Event event) 44 | { 45 | System.out.println("Received event: " + event); 46 | } 47 | }; 48 | session.addHandler(handler); 49 | 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /example-games/src/main/java/io/nadron/example/zombie/game/ZombieRoom.java: -------------------------------------------------------------------------------- 1 | package io.nadron.example.zombie.game; 2 | 3 | import io.nadron.app.PlayerSession; 4 | import io.nadron.app.impl.GameRoomSession; 5 | import io.nadron.example.zombie.domain.Defender; 6 | import io.nadron.example.zombie.domain.IAM; 7 | import io.nadron.example.zombie.domain.World; 8 | import io.nadron.example.zombie.domain.Zombie; 9 | 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | 14 | public class ZombieRoom extends GameRoomSession 15 | { 16 | private static final Logger LOG = LoggerFactory.getLogger(ZombieRoom.class); 17 | 18 | private Defender defender; 19 | private Zombie zombie; 20 | 21 | public ZombieRoom(GameRoomSessionBuilder sessionBuilder) 22 | { 23 | super(sessionBuilder); 24 | } 25 | 26 | public ZombieRoom(GameRoomSessionBuilder sessionBuilder, World world, Defender defender, Zombie zombie) 27 | { 28 | super(sessionBuilder); 29 | this.defender = defender; 30 | this.zombie = zombie; 31 | } 32 | 33 | @Override 34 | public void onLogin(PlayerSession playerSession) 35 | { 36 | SessionHandler listener = new SessionHandler(playerSession,defender, zombie, 37 | IAM.ZOMBIE); 38 | playerSession.addHandler(listener); 39 | LOG.trace("Added event listener in Zombie Room"); 40 | } 41 | 42 | public Defender getDefender() 43 | { 44 | return defender; 45 | } 46 | 47 | public void setDefender(Defender defender) 48 | { 49 | this.defender = defender; 50 | } 51 | 52 | public Zombie getZombie() 53 | { 54 | return zombie; 55 | } 56 | 57 | public void setZombie(Zombie zombie) 58 | { 59 | this.zombie = zombie; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/convert/flex/SerializationContextProvider.java: -------------------------------------------------------------------------------- 1 | package io.nadron.convert.flex; 2 | 3 | import flex.messaging.io.SerializationContext; 4 | 5 | /** 6 | * This class provides threadlocal contexts on demand to the serializer and 7 | * deserializer class. This context object is necessary for blazeds to do 8 | * serialization. 9 | * 10 | * @author Abraham Menacherry 11 | * 12 | */ 13 | public class SerializationContextProvider { 14 | 15 | public SerializationContext get() 16 | { 17 | // Threadlocal SerializationContent 18 | SerializationContext serializationContext = SerializationContext 19 | .getSerializationContext(); 20 | serializationContext.enableSmallMessages = true; 21 | serializationContext.instantiateTypes = true; 22 | // use _remoteClass field 23 | serializationContext.supportRemoteClass = true; 24 | // false Legacy Flex 1.5 behavior was to return a java.util.Collection 25 | // for Array, New Flex 2+ behavior is to return Object[] for AS3 Array 26 | serializationContext.legacyCollection = false; 27 | // false Legacy flash.xml.XMLDocument Type 28 | serializationContext.legacyMap = false; 29 | // true New E4X XML Type 30 | serializationContext.legacyXMLDocument = false; 31 | // determines whether the constructed Document is name-space aware 32 | serializationContext.legacyXMLNamespaces = false; 33 | serializationContext.legacyThrowable = false; 34 | serializationContext.legacyBigNumbers = false; 35 | serializationContext.restoreReferences = false; 36 | serializationContext.logPropertyErrors = false; 37 | serializationContext.ignorePropertyErrors = true; 38 | return serializationContext; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/service/impl/SimpleLookupService.java: -------------------------------------------------------------------------------- 1 | package io.nadron.service.impl; 2 | 3 | import io.nadron.app.Game; 4 | import io.nadron.app.GameRoom; 5 | import io.nadron.app.Player; 6 | import io.nadron.app.impl.DefaultPlayer; 7 | import io.nadron.service.LookupService; 8 | import io.nadron.util.Credentials; 9 | 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | 14 | 15 | /** 16 | * The lookup service abstracts away the implementation detail on getting the 17 | * game objects from the reference key provided by the client. This lookup is 18 | * now done from a hashmap but can be done from database or any other manner. 19 | * 20 | * @author Abraham Menacherry 21 | * 22 | */ 23 | public class SimpleLookupService implements LookupService 24 | { 25 | private final Map refKeyGameRoomMap; 26 | 27 | public SimpleLookupService() 28 | { 29 | refKeyGameRoomMap = new HashMap(); 30 | } 31 | 32 | public SimpleLookupService(Map refKeyGameRoomMap) 33 | { 34 | super(); 35 | this.refKeyGameRoomMap = refKeyGameRoomMap; 36 | } 37 | 38 | @Override 39 | public Game gameLookup(Object gameContextKey) 40 | { 41 | // TODO Auto-generated method stub 42 | return null; 43 | } 44 | 45 | @Override 46 | public GameRoom gameRoomLookup(Object gameContextKey) 47 | { 48 | return refKeyGameRoomMap.get((String) gameContextKey); 49 | } 50 | 51 | @Override 52 | public Player playerLookup(Credentials loginDetail) 53 | { 54 | return new DefaultPlayer(); 55 | } 56 | 57 | public Map getRefKeyGameRoomMap() 58 | { 59 | return refKeyGameRoomMap; 60 | } 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /jetclient-as3/src/main/flex/io/nadron/codecs/impl/DefaultCodecChain.as: -------------------------------------------------------------------------------- 1 | package io.nadron.codecs.impl 2 | { 3 | import io.nadron.codecs.CodecChain; 4 | import io.nadron.codecs.Transform; 5 | 6 | /** 7 | * Default implementation of a codecchain which uses an array 8 | * to store the list of transformers. 9 | * @author Abraham Menacherry 10 | */ 11 | public class DefaultCodecChain implements CodecChain 12 | { 13 | private var chain:Array; 14 | 15 | public function DefaultCodecChain() 16 | { 17 | chain = new Array(); 18 | } 19 | 20 | public function add(next:Transform):void 21 | { 22 | chain.push(next); 23 | } 24 | 25 | public function remove(next:Transform):void 26 | { 27 | chain.splice(chain.indexOf(next),1); 28 | } 29 | 30 | /** 31 | * Can be considered to be a composite method, which will call transform on all the 32 | * component transformers in the array and then return the final decoded or encoded object. 33 | * 34 | * @param input An object that needs to be transformed to another. 35 | * @return The transformed object. If any transformer within the chain cannot transform the 36 | * object passed in to it, the it will return null and the method will also return null. 37 | */ 38 | public function transform(input:Object):Object 39 | { 40 | var len:uint = chain.length; 41 | for (var i:uint = 0; i < len; i++) { 42 | var output:Object = chain[i].transform(input); 43 | if (null == output) { 44 | return null; 45 | }else { 46 | input = output; 47 | } 48 | } 49 | 50 | return input; 51 | } 52 | 53 | public function getChain():Array 54 | { 55 | return chain; 56 | } 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/handlers/netty/UDPPipelineFactory.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.handlers.netty; 2 | 3 | import io.netty.channel.ChannelHandler; 4 | import io.netty.channel.ChannelInitializer; 5 | import io.netty.channel.ChannelPipeline; 6 | import io.netty.channel.socket.DatagramChannel; 7 | 8 | import java.net.InetSocketAddress; 9 | 10 | public class UDPPipelineFactory extends ChannelInitializer 11 | { 12 | public static final String EVENT_ENCODER_NAME = "eventEncoder"; 13 | private static UDPUpstreamHandler UDP_UPSTREAM_HANDLER; 14 | private static UDPPipelineFactory INSTANCE; 15 | private static UDPEventEncoder udpEventEncoder; 16 | static { 17 | UDP_UPSTREAM_HANDLER = new UDPUpstreamHandler(); 18 | } 19 | 20 | private InetSocketAddress udpServerAddress; 21 | public UDPPipelineFactory(InetSocketAddress udpServerAddress) 22 | { 23 | this.udpServerAddress = udpServerAddress; 24 | } 25 | 26 | @Override 27 | protected void initChannel(DatagramChannel ch) throws Exception 28 | { 29 | ChannelPipeline pipeline = ch.pipeline(); 30 | pipeline.addLast(EVENT_ENCODER_NAME, getEventEncoder(udpServerAddress)); 31 | pipeline.addLast("UDPUpstreamHandler",UDP_UPSTREAM_HANDLER); 32 | } 33 | 34 | public synchronized static UDPPipelineFactory getInstance(InetSocketAddress udpServerAddress) 35 | { 36 | if(null == INSTANCE) 37 | { 38 | INSTANCE = new UDPPipelineFactory(udpServerAddress); 39 | } 40 | return INSTANCE; 41 | } 42 | 43 | public synchronized static ChannelHandler getEventEncoder(InetSocketAddress udpServerAddress){ 44 | if(null == udpEventEncoder){ 45 | udpEventEncoder = new UDPEventEncoder(udpServerAddress); 46 | } 47 | return udpEventEncoder; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/app/GameStartListener.java: -------------------------------------------------------------------------------- 1 | package io.nadron.app; 2 | 3 | import java.util.Properties; 4 | 5 | /** 6 | * Defines the starting point for a game instance. This method will be called by 7 | * the container one time for each startup. Subsequent startups for the same 8 | * game instance which was persisted would have the isInitialized set to true. 9 | * 10 | * @author Abraham Menacherry 11 | * 12 | */ 13 | public interface GameStartListener 14 | { 15 | /** 16 | * The "public static void main" for each game instance( a game instance 17 | * would actually take place in a game room). This method can be used to 18 | * load all the initial state of the {@link Game} or {@link GameRoom}, 19 | * start up scheduled {@link Task}s and do any other initialization logic 20 | * necessary for a particular game. 21 | * 22 | * @param isInitialized 23 | * If this is a persisted instance, then restarts would be called 24 | * with isInitialized set to true. TODO current version does not 25 | * support persistence and hence this parameter will always be 26 | * false. 27 | * @param properties 28 | * The environment properties which would be a combination of JVM 29 | * flags and properties files would be passed into this method by 30 | * the container 31 | */ 32 | public void start(boolean isInitialized, Properties properties); 33 | 34 | /** 35 | * @return Returns the associated game room instance. 36 | */ 37 | public GameRoom getGameRoom(); 38 | 39 | /** 40 | * @param gameRoom 41 | * The game room instance to set on the implementation. 42 | */ 43 | public void setGameRoom(GameRoom gameRoom); 44 | } 45 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/event/impl/DefaultEvent.java: -------------------------------------------------------------------------------- 1 | package io.nadron.event.impl; 2 | 3 | import io.nadron.event.Event; 4 | import io.nadron.event.EventContext; 5 | 6 | import java.io.Serializable; 7 | 8 | 9 | 10 | public class DefaultEvent implements Event, Serializable 11 | { 12 | /** 13 | * Eclipse Generated serial version id. 14 | */ 15 | private static final long serialVersionUID = 8188757584720622237L; 16 | 17 | protected EventContext eventContext; 18 | protected int type; 19 | protected Object source; 20 | protected long timeStamp; 21 | private String cName; 22 | 23 | @Override 24 | public EventContext getEventContext() 25 | { 26 | return eventContext; 27 | } 28 | 29 | @Override 30 | public int getType() 31 | { 32 | return type; 33 | } 34 | 35 | @Override 36 | public Object getSource() 37 | { 38 | return source; 39 | } 40 | 41 | @Override 42 | public long getTimeStamp() 43 | { 44 | return timeStamp; 45 | } 46 | 47 | @Override 48 | public void setEventContext(EventContext context) 49 | { 50 | this.eventContext = context; 51 | } 52 | 53 | @Override 54 | public void setType(int type) 55 | { 56 | this.type = type; 57 | } 58 | 59 | @Override 60 | public void setSource(Object source) 61 | { 62 | this.source = source; 63 | } 64 | 65 | @Override 66 | public void setTimeStamp(long timeStamp) 67 | { 68 | this.timeStamp = timeStamp; 69 | 70 | } 71 | 72 | @Override 73 | public String toString() { 74 | return "Event [type=" + type + ", source=" + source + ", timeStamp=" 75 | + timeStamp + "]"; 76 | } 77 | 78 | public String getcName() 79 | { 80 | return cName; 81 | } 82 | 83 | public void setcName(String cName) 84 | { 85 | this.cName = cName; 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/protocols/AbstractNettyProtocol.java: -------------------------------------------------------------------------------- 1 | package io.nadron.protocols; 2 | 3 | import io.nadron.app.PlayerSession; 4 | import io.nadron.util.NettyUtils; 5 | import io.netty.channel.ChannelPipeline; 6 | import io.netty.handler.codec.LengthFieldBasedFrameDecoder; 7 | 8 | 9 | /** 10 | * This abstract class defines common methods across all protocols. Individual 11 | * protocol classes extend this class. 12 | * 13 | * @author Abraham Menacherry 14 | * 15 | */ 16 | public abstract class AbstractNettyProtocol implements Protocol 17 | { 18 | /** 19 | * The name of the protocol. This is set by the child class to appropriate 20 | * value while child class instance is created. 21 | */ 22 | final String protocolName; 23 | 24 | /** 25 | * Name of the idle state check handlers which will be removed by protocol 26 | * manually if required from pipeline. 27 | */ 28 | public static final String IDLE_STATE_CHECK_HANDLER = "idleStateCheck"; 29 | 30 | public AbstractNettyProtocol(String protocolName) 31 | { 32 | super(); 33 | this.protocolName = protocolName; 34 | } 35 | 36 | public LengthFieldBasedFrameDecoder createLengthBasedFrameDecoder() 37 | { 38 | return new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 2, 0, 2); 39 | } 40 | 41 | @Override 42 | public String getProtocolName() 43 | { 44 | return protocolName; 45 | } 46 | 47 | @Override 48 | public void applyProtocol(PlayerSession playerSession, 49 | boolean clearExistingProtocolHandlers) 50 | { 51 | if(clearExistingProtocolHandlers) 52 | { 53 | ChannelPipeline pipeline = NettyUtils 54 | .getPipeLineOfConnection(playerSession); 55 | NettyUtils.clearPipeline(pipeline); 56 | } 57 | applyProtocol(playerSession); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /nadclient/README.md: -------------------------------------------------------------------------------- 1 | # Nadron Client 2 | 3 | This is a Java client library for the [Nadron](https://github.com/menacher/java-game-server/tree/netty4/nadron) game server. 4 | 5 | ## Requirements 6 | 7 | - **Java 17 or later** 8 | - **Maven 3.8+** (for building) 9 | 10 | ## Maven Dependency 11 | 12 | ```xml 13 | 14 | com.github.menacher 15 | nadclient 16 | 0.8-SNAPSHOT 17 | 18 | ``` 19 | 20 | ## Technology Stack 21 | 22 | - **Java 17 LTS** 23 | - **Netty 4.1.115** - High-performance async network framework 24 | - **JUnit 5.11.3** - Modern testing framework 25 | 26 | ## Usage as Game Client 27 | 28 | The general usage steps: 29 | 30 | 1. Add `nadclient-0.8-SNAPSHOT.jar` and its dependencies to your project classpath (or use Maven) 31 | 2. Create `LoginBuilder`, session and `SessionEventHandler` as shown in test classes 32 | 3. The `example-games` project has a `ZombieNadClient` which demonstrates a complete example 33 | 4. Use the `SessionEventHandler` to accept events from the remote server and to send events back 34 | 5. To send to the remote Nadron server, create a TCP/UDP network event: 35 | ```java 36 | Event event = Events.networkEvent(messageBuffer); 37 | // or for UDP 38 | Events.networkEvent(messageBuffer, DeliveryGuaranty.Fast); 39 | ``` 40 | 6. Call `session.onEvent(event);` to transmit the event to the remote Nadron server 41 | 42 | ## Building from Source 43 | 44 | ```bash 45 | git clone git@github.com:menacher/java-game-server.git 46 | cd java-game-server/nadclient 47 | mvn clean install 48 | ``` 49 | 50 | ## Running Tests 51 | 52 | ```bash 53 | mvn test 54 | ``` 55 | 56 | All 7 tests pass successfully on Java 17 with Netty 4.1. -------------------------------------------------------------------------------- /example-games/src/test/java/io/nadron/example/zombie/WriteByte.java: -------------------------------------------------------------------------------- 1 | package io.nadron.example.zombie; 2 | 3 | import io.nadron.event.Events; 4 | import io.nadron.example.zombie.domain.IAM; 5 | import io.nadron.example.zombie.domain.ZombieCommands; 6 | import io.netty.buffer.ByteBuf; 7 | import io.netty.buffer.Unpooled; 8 | import io.netty.channel.Channel; 9 | import io.netty.channel.socket.DatagramChannel; 10 | 11 | import java.net.SocketAddress; 12 | import java.util.TimerTask; 13 | 14 | 15 | 16 | public class WriteByte extends TimerTask 17 | { 18 | 19 | Channel channel; 20 | IAM iam; 21 | SocketAddress remoteAddress; 22 | 23 | public WriteByte(Channel channel,SocketAddress remoteAddress,IAM iam) 24 | { 25 | this.channel = channel; 26 | this.remoteAddress = remoteAddress; 27 | this.iam = iam; 28 | } 29 | 30 | @Override 31 | public void run() 32 | { 33 | int type = IAM.getInt(iam); 34 | int operation = 0; 35 | switch(iam) 36 | { 37 | case DEFENDER: 38 | operation = ZombieCommands.SHOT_GUN.getCommand(); 39 | break; 40 | case ZOMBIE: 41 | operation = ZombieCommands.EAT_BRAINS.getCommand(); 42 | break; 43 | } 44 | ByteBuf buf = null; 45 | if(null == remoteAddress){ 46 | //TCP 47 | for(int i =0; i < 1;i++){ 48 | buf = Unpooled.buffer(1 + 8); 49 | buf.writeByte(Events.SESSION_MESSAGE); 50 | buf.writeInt(type); 51 | buf.writeInt(operation); 52 | channel.write(buf); 53 | } 54 | } 55 | else 56 | { 57 | //UDP 58 | DatagramChannel udpChannel = (DatagramChannel)channel; 59 | buf = Unpooled.buffer(1 + 8); 60 | buf.writeByte(Events.SESSION_MESSAGE); 61 | buf.writeInt(type); 62 | buf.writeInt(operation); 63 | for(int i =0; i < 1;i++){ 64 | udpChannel.write(buf); 65 | } 66 | } 67 | 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /jetclient-as3/src/main/flex/io/nadron/app/Session.as: -------------------------------------------------------------------------------- 1 | package io.nadron.app 2 | { 3 | import flash.events.EventDispatcher; 4 | import flash.events.IEventDispatcher; 5 | import io.nadron.codecs.CodecChain; 6 | import io.nadron.communication.MessageSender; 7 | import io.nadron.event.NadEvent; 8 | import flash.events.Event; 9 | import io.nadron.codecs.InAndOutCodecChain; 10 | 11 | /** 12 | * This interface abstracts a session in jetclient. A session can be thought of as a high 13 | * level connection to a remote nadron server over TCP. The session also has event dispatching 14 | * capabilities. So when an event comes into the session, it will get dispatched to the 15 | * appropriate event listener. 16 | * 17 | * @author Abraham Menacherry 18 | * 19 | */ 20 | public interface Session extends IEventDispatcher 21 | { 22 | function messageReceived(event:Event):void; 23 | function sendToServer(message:Object):void; 24 | function getId():Object; 25 | function setId(id:Object):void; 26 | function getAttribute(key:String):Object; 27 | function setAttribute(key:String, value:Object):void; 28 | function removeAttribute(key:String):void; 29 | function getCreationTime():Number; 30 | function setCreationTime(time:Number):void 31 | function isWriteable():Boolean; 32 | function setWriteable(writeable:Boolean = true):void; 33 | function close():void; 34 | function isShuttingDown():Boolean; 35 | function setShuttingDown(shutDown:Boolean = true):void; 36 | function getMessageSender():MessageSender; 37 | function setMessageSender(messageSender:MessageSender):void; 38 | function getInCodecs():CodecChain; 39 | function setInCodecs(inCodecs:CodecChain):void 40 | function getOutCodecs():CodecChain; 41 | function setOutCodecs(outCodecs:CodecChain):void; 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/convert/flex/AMFDeSerializer.java: -------------------------------------------------------------------------------- 1 | package io.nadron.convert.flex; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.IOException; 5 | 6 | import flex.messaging.io.SerializationContext; 7 | import flex.messaging.io.amf.Amf3Input; 8 | 9 | /** 10 | * This class is used to convert an AMF3 object (received as a byte array) to a 11 | * Java object. It uses AMF3Input and SerializationContext classes to achieve 12 | * this functionality. 13 | * 14 | * @author Abraham Menacherry 15 | * 16 | */ 17 | public class AMFDeSerializer { 18 | 19 | /** 20 | * The serialization context used as an input for the AMF3Input class. 21 | */ 22 | private SerializationContext context; 23 | 24 | public AMFDeSerializer(SerializationContext serializationContext) { 25 | this.context = serializationContext; 26 | } 27 | 28 | /** 29 | * This method takes an AMF3 object in byte array form and converts it to a 30 | * corresponding java object. 31 | * 32 | * @param 33 | * @param amf 34 | * The serialized AMF3 object as a byte array. 35 | * @return Returns the java object after conversion. 36 | * @throws ClassNotFoundException 37 | * @throws IOException 38 | */ 39 | @SuppressWarnings("unchecked") 40 | public T fromAmf(final ByteArrayInputStream amf) 41 | throws ClassNotFoundException, IOException { 42 | Amf3Input amf3Input = new Amf3Input(context); 43 | amf3Input.setInputStream(amf); 44 | // Read object does the actual work of conversion. 45 | T object = (T) amf3Input.readObject(); 46 | amf3Input.close(); 47 | return object; 48 | } 49 | 50 | public SerializationContext getContext() { 51 | return context; 52 | } 53 | 54 | public void setContext(SerializationContext context) { 55 | this.context = context; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/server/netty/ProtocolMultiplexerChannelInitializer.java: -------------------------------------------------------------------------------- 1 | package io.nadron.server.netty; 2 | 3 | import io.nadron.handlers.netty.LoginProtocol; 4 | import io.nadron.handlers.netty.ProtocolMultiplexerDecoder; 5 | import io.netty.channel.ChannelHandler; 6 | import io.netty.channel.ChannelInitializer; 7 | import io.netty.channel.ChannelPipeline; 8 | import io.netty.channel.socket.SocketChannel; 9 | import io.netty.handler.timeout.IdleStateHandler; 10 | 11 | 12 | public class ProtocolMultiplexerChannelInitializer extends 13 | ChannelInitializer 14 | { 15 | // TODO make this configurable from spring. 16 | private static final int MAX_IDLE_SECONDS = 60; 17 | private int bytesForProtocolCheck; 18 | private LoginProtocol loginProtocol; 19 | 20 | @Override 21 | protected void initChannel(SocketChannel ch) throws Exception 22 | { 23 | // Create a default pipeline implementation. 24 | ChannelPipeline pipeline = ch.pipeline(); 25 | pipeline.addLast("idleStateCheck", new IdleStateHandler( 26 | MAX_IDLE_SECONDS, MAX_IDLE_SECONDS, MAX_IDLE_SECONDS)); 27 | pipeline.addLast("multiplexer", createProtcolMultiplexerDecoder()); 28 | } 29 | 30 | protected ChannelHandler createProtcolMultiplexerDecoder() 31 | { 32 | return new ProtocolMultiplexerDecoder(bytesForProtocolCheck, loginProtocol); 33 | } 34 | 35 | public int getBytesForProtocolCheck() 36 | { 37 | return bytesForProtocolCheck; 38 | } 39 | 40 | public void setBytesForProtocolCheck(int bytesForProtocolCheck) 41 | { 42 | this.bytesForProtocolCheck = bytesForProtocolCheck; 43 | } 44 | 45 | public LoginProtocol getLoginProtocol() 46 | { 47 | return loginProtocol; 48 | } 49 | 50 | public void setLoginProtocol(LoginProtocol loginProtocol) 51 | { 52 | this.loginProtocol = loginProtocol; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /example-games/src/main/java/io/nadron/example/lostdecade/LDGameState.java: -------------------------------------------------------------------------------- 1 | package io.nadron.example.lostdecade; 2 | 3 | import io.nadron.app.GameRoom; 4 | 5 | import java.io.Serializable; 6 | import java.util.Set; 7 | 8 | 9 | 10 | /** 11 | * The state of a game room is held in this object. Multiple remote client 12 | * connections to a {@link GameRoom} will share this state. 13 | * 14 | * @author Abraham Menacherry 15 | * 16 | */ 17 | public class LDGameState implements Serializable 18 | { 19 | /** 20 | * 21 | */ 22 | private static final long serialVersionUID = 1L; 23 | private Set entities; 24 | private Entity monster; 25 | private Entity hero; 26 | private boolean reset; 27 | 28 | public LDGameState() 29 | { 30 | 31 | } 32 | 33 | public LDGameState(Set entities, Entity monster, Entity hero) 34 | { 35 | super(); 36 | this.entities = entities; 37 | this.monster = monster; 38 | this.hero = hero; 39 | } 40 | 41 | public Entity getMonster() 42 | { 43 | return monster; 44 | } 45 | 46 | public void setMonster(Entity monster) 47 | { 48 | this.monster = monster; 49 | } 50 | 51 | public void addEntitiy(Entity hero) 52 | { 53 | // only the id will match, but other values maybe different. 54 | entities.remove(hero); 55 | entities.add(hero); 56 | } 57 | 58 | public Entity getHero() 59 | { 60 | return hero; 61 | } 62 | 63 | public void setHero(Entity hero) 64 | { 65 | this.hero = hero; 66 | } 67 | 68 | public boolean isReset() 69 | { 70 | return reset; 71 | } 72 | 73 | public void setReset(boolean reset) 74 | { 75 | this.reset = reset; 76 | } 77 | 78 | public Set getEntities() 79 | { 80 | return entities; 81 | } 82 | 83 | public void setEntities(Set entities) 84 | { 85 | this.entities = entities; 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/handlers/netty/MessageBufferEventEncoder.java: -------------------------------------------------------------------------------- 1 | package io.nadron.handlers.netty; 2 | 3 | import io.nadron.communication.MessageBuffer; 4 | import io.nadron.event.Event; 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.buffer.Unpooled; 7 | import io.netty.channel.ChannelHandler.Sharable; 8 | import io.netty.channel.ChannelHandlerContext; 9 | import io.netty.handler.codec.MessageToMessageEncoder; 10 | 11 | import java.util.List; 12 | 13 | 14 | 15 | @Sharable 16 | public class MessageBufferEventEncoder extends MessageToMessageEncoder 17 | { 18 | 19 | @Override 20 | protected void encode(ChannelHandlerContext ctx, Event event, 21 | List out) throws Exception 22 | { 23 | out.add(encode(ctx, event)); 24 | } 25 | 26 | /** 27 | * Encode is separated out so that child classes can still reuse this 28 | * functionality. 29 | * 30 | * @param ctx 31 | * @param event 32 | * The event to be encoded into {@link ByteBuf}. It will be 33 | * converted to 'opcode'-'payload' format. 34 | * @return If only opcode is specified a single byte {@link ByteBuf} is 35 | * returned, otherwise a byte buf with 'opcode'-'payload' format is 36 | * returned. 37 | */ 38 | protected ByteBuf encode(ChannelHandlerContext ctx, Event event) 39 | { 40 | ByteBuf msg = null; 41 | if(null != event.getSource()) 42 | { 43 | @SuppressWarnings("unchecked") 44 | MessageBuffer msgBuffer = (MessageBuffer)event.getSource(); 45 | ByteBuf data = msgBuffer.getNativeBuffer(); 46 | ByteBuf opcode = ctx.alloc().buffer(1); 47 | opcode.writeByte(event.getType()); 48 | msg = Unpooled.wrappedBuffer(opcode, data); 49 | } 50 | else 51 | { 52 | msg = ctx.alloc().buffer(1); 53 | msg.writeByte(event.getType()); 54 | } 55 | return msg; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/communication/MessageSender.java: -------------------------------------------------------------------------------- 1 | package io.nadron.communication; 2 | 3 | /** 4 | * This interface declares method for sending a message to client. Different 5 | * implementations would be used by the server for sending based on the delivery 6 | * guaranty that is required. 7 | * 8 | * @author Abraham Menacherry 9 | * 10 | */ 11 | public interface MessageSender 12 | { 13 | /** 14 | * This method delegates to the underlying native session object to send a 15 | * message to the client. 16 | * 17 | * @param message 18 | * The message to be sent to client. 19 | * @return The boolean or future associated with this operation if 20 | * synchronous or asynchronous implementation respectively. 21 | */ 22 | public Object sendMessage(Object message); 23 | 24 | /** 25 | * Returns the delivery guaranty of the implementation. Currently only 26 | * RELIABLE and FAST are supported, their respective integer values are 0 27 | * and 1. 28 | * 29 | * @return The guaranty instance associated with the implementation. 30 | */ 31 | public DeliveryGuaranty getDeliveryGuaranty(); 32 | 33 | /** 34 | * Since message sender would have a network connection, it would require 35 | * some cleanup. This method can be overriden to close underlying channels 36 | * and so on. 37 | */ 38 | public void close(); 39 | 40 | /** 41 | * An interface whose implementations would transmit messages reliably to 42 | * the remote machine/vm. The transport for instance could be TCP. 43 | * 44 | * @author Abraham Menacherry 45 | * 46 | */ 47 | public interface Reliable extends MessageSender{} 48 | 49 | /** 50 | * An interface whose implementations would transmit messages fast but 51 | * unreliably to the remote machine/vm. The transport for instance 52 | * could be UDP. 53 | * 54 | * @author Abraham Menacherry 55 | * 56 | */ 57 | public interface Fast extends MessageSender{} 58 | 59 | } 60 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/concurrent/DataFlowVariable.java: -------------------------------------------------------------------------------- 1 | package io.nadron.concurrent; 2 | 3 | import java.util.concurrent.CountDownLatch; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | /** 7 | * An anemic implementation of Gpars GPars dataflow variable. This class 9 | * will block the getVal call using a simple count down latch, also it does not 10 | * really prevent resetting the value using bind. 11 | * 12 | * @author Abraham Menacherry 13 | * 14 | */ 15 | public class DataFlowVariable 16 | { 17 | final CountDownLatch latch; 18 | Object val = null; 19 | 20 | public DataFlowVariable() 21 | { 22 | this.latch = new CountDownLatch(1); 23 | } 24 | 25 | public DataFlowVariable(CountDownLatch latch) 26 | { 27 | this.latch = latch; 28 | } 29 | 30 | /** 31 | * The method will bind the incoming value to the value in the class and 32 | * then do a countDown on the latch. 33 | * 34 | * @param val 35 | */ 36 | public void bind(Object val) 37 | { 38 | this.val = val; 39 | latch.countDown(); 40 | } 41 | 42 | /** 43 | * This method blocks till the count down latch has reset to 0. 44 | * 45 | * @return Returns the value set by the bind method 46 | * @throws InterruptedException 47 | */ 48 | public Object getVal() throws InterruptedException 49 | { 50 | latch.await(); 51 | return val; 52 | } 53 | 54 | /** 55 | * This method blocks for a specified amount of time to retrieve the value 56 | * bound in bind method. 57 | * 58 | * @param waitTime 59 | * the amount of time to wait 60 | * @param timeUnit 61 | * the unit, milliseconds, seconds etc. 62 | * @return Returns the bound value or null if the time out has exceeded. 63 | * @throws InterruptedException 64 | */ 65 | public Object getVal(long waitTime, TimeUnit timeUnit) 66 | throws InterruptedException 67 | { 68 | if(latch.await(waitTime, timeUnit)){ 69 | return val; 70 | } 71 | else 72 | { 73 | return null; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/service/impl/SimpleGameAdminService.java: -------------------------------------------------------------------------------- 1 | package io.nadron.service.impl; 2 | 3 | import io.nadron.app.Game; 4 | import io.nadron.app.GameRoom; 5 | import io.nadron.service.GameAdminService; 6 | import io.netty.channel.group.ChannelGroupFuture; 7 | 8 | import java.util.Collection; 9 | 10 | 11 | 12 | public class SimpleGameAdminService implements GameAdminService 13 | { 14 | private Collection games; 15 | 16 | @Override 17 | public boolean registerGame(Game game) 18 | { 19 | return games.add(game); 20 | } 21 | 22 | @Override 23 | public Object loadGame(long gameId, String gameName) 24 | { 25 | // TODO Auto-generated method stub 26 | return null; 27 | } 28 | 29 | @Override 30 | public Object loadGameRoom(Game game, long gameRoomId, String gameRoomName) 31 | { 32 | // TODO Auto-generated method stub 33 | return null; 34 | } 35 | 36 | @Override 37 | public Object unLoadGame(long gameId, String gameName) 38 | { 39 | // TODO Auto-generated method stub 40 | return null; 41 | } 42 | 43 | @Override 44 | public Object unLoadGame(Game game) 45 | { 46 | if(null != game){ 47 | ChannelGroupFuture groupFuture = (ChannelGroupFuture)game.unload(); 48 | return groupFuture; 49 | } 50 | return null; 51 | } 52 | 53 | @Override 54 | public void unloadGameRoom(GameRoom gameRoom) 55 | { 56 | if(null != gameRoom){ 57 | gameRoom.close(); 58 | } 59 | } 60 | 61 | @Override 62 | public Object unloadGameRoom(Game game, long gameRoomId) 63 | { 64 | return null; 65 | } 66 | 67 | @Override 68 | public Object unloadGameRoom(Game game, String gameRoomId) 69 | { 70 | return null; 71 | } 72 | 73 | @Override 74 | public synchronized void shutdown() 75 | { 76 | if(null != games) 77 | { 78 | for (Game game: games) 79 | { 80 | unLoadGame(game); 81 | } 82 | } 83 | } 84 | 85 | public Collection getGames() 86 | { 87 | return games; 88 | } 89 | 90 | public void setGames(Collection games) 91 | { 92 | this.games = games; 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /jetclient-as3/src/main/flex/io/nadron/protocol/impl/AMF3Protocol.as: -------------------------------------------------------------------------------- 1 | package io.nadron.protocol.impl 2 | { 3 | import flash.net.Socket; 4 | import io.nadron.app.Session; 5 | import io.nadron.codecs.CodecChain; 6 | import io.nadron.codecs.impl.AMFDeserializer; 7 | import io.nadron.codecs.impl.AMFSerializer; 8 | import io.nadron.codecs.impl.DefaultCodecChain; 9 | import io.nadron.codecs.impl.LengthFieldBasedFrameDecoder; 10 | import io.nadron.codecs.impl.LengthFieldPrepender; 11 | import io.nadron.communication.DefaultMessageSender; 12 | import io.nadron.communication.MessageSender; 13 | import io.nadron.protocol.Protocol; 14 | 15 | /** 16 | * This protocol has encoders and decoders which do AMF3 (de)serialization while 17 | * transmitting and receiving messages to remote Nadron server. 18 | * @author Abraham Menacherry 19 | */ 20 | public class AMF3Protocol implements Protocol 21 | { 22 | public static const name:String = "AMF3Protocol"; 23 | private var lengthFieldPrepender:LengthFieldPrepender; 24 | private var amfSerializer:AMFSerializer; 25 | private var amfDeSerializer:AMFDeserializer; 26 | 27 | 28 | public function AMF3Protocol() 29 | { 30 | lengthFieldPrepender = new LengthFieldPrepender(); 31 | amfSerializer = new AMFSerializer(); 32 | amfDeSerializer = new AMFDeserializer(); 33 | } 34 | 35 | public function getName():String 36 | { 37 | return name; 38 | } 39 | 40 | public function applyProtocol(session:Session):void 41 | { 42 | var inCodec:CodecChain = new DefaultCodecChain(); 43 | inCodec.add(new LengthFieldBasedFrameDecoder()); 44 | inCodec.add(amfDeSerializer); 45 | session.setInCodecs(inCodec); 46 | } 47 | 48 | public function createMessageSender(socket:Socket):MessageSender 49 | { 50 | var outCodec:CodecChain = new DefaultCodecChain(); 51 | outCodec.add(amfSerializer); 52 | outCodec.add(lengthFieldPrepender); 53 | var messageSender:MessageSender = new DefaultMessageSender(socket, outCodec); 54 | return messageSender; 55 | } 56 | 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /example-games/src/main/java/io/nadron/example/zombie/domain/WorldMonitor.java: -------------------------------------------------------------------------------- 1 | package io.nadron.example.zombie.domain; 2 | 3 | import io.nadron.app.GameRoom; 4 | import io.nadron.app.Task; 5 | import io.nadron.communication.NettyMessageBuffer; 6 | import io.nadron.communication.DeliveryGuaranty.DeliveryGuarantyOptions; 7 | import io.nadron.event.Events; 8 | import io.nadron.event.NetworkEvent; 9 | import io.nadron.example.zombie.game.Messages; 10 | import io.nadron.protocols.impl.WebSocketProtocol; 11 | 12 | 13 | public class WorldMonitor implements Task 14 | { 15 | private World world; 16 | private GameRoom room; 17 | 18 | private Object id; 19 | 20 | public WorldMonitor(World world, GameRoom room) 21 | { 22 | this.world = world; 23 | this.room = room; 24 | } 25 | 26 | public World getWorld() 27 | { 28 | return world; 29 | } 30 | 31 | public void setWorld(World world) 32 | { 33 | this.world = world; 34 | } 35 | 36 | @Override 37 | public Object getId() 38 | { 39 | return id; 40 | } 41 | 42 | @Override 43 | public void run() 44 | { 45 | if(world.apocalypse()) 46 | { 47 | // Send it to all players 48 | System.out.println("Apocalypse is here"); 49 | NetworkEvent networkEvent = Events.networkEvent(Messages.apocalypse()); 50 | room.sendBroadcast(networkEvent); 51 | } 52 | else 53 | { 54 | NetworkEvent networkEvent = null; 55 | if(room.getProtocol() instanceof WebSocketProtocol) 56 | { 57 | networkEvent = Events.networkEvent(world.getAlive()); 58 | } 59 | else 60 | { 61 | NettyMessageBuffer buffer = new NettyMessageBuffer(); 62 | buffer.writeInt(world.getAlive()); 63 | networkEvent = Events.networkEvent(buffer,DeliveryGuarantyOptions.FAST); 64 | } 65 | room.sendBroadcast(networkEvent); 66 | } 67 | 68 | world.report(); 69 | } 70 | 71 | @Override 72 | public void setId(Object id) 73 | { 74 | this.id = id; 75 | } 76 | 77 | public GameRoom getRoom() 78 | { 79 | return room; 80 | } 81 | 82 | public void setRoom(GameRoom room) 83 | { 84 | this.room = room; 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/server/netty/UDPChannelInitializer.java: -------------------------------------------------------------------------------- 1 | package io.nadron.server.netty; 2 | 3 | import io.nadron.handlers.netty.UDPEventEncoder; 4 | import io.nadron.handlers.netty.UDPUpstreamHandler; 5 | import io.netty.channel.ChannelInitializer; 6 | import io.netty.channel.ChannelPipeline; 7 | import io.netty.channel.socket.DatagramChannel; 8 | 9 | 10 | 11 | public class UDPChannelInitializer extends ChannelInitializer 12 | { 13 | /** 14 | * This pipeline will be shared across all the channels. In Netty UDP 15 | * implementation it does not make sense to have different pipelines for 16 | * different channels as the protocol is essentially "connection-less" 17 | */ 18 | ChannelPipeline pipeline; 19 | private UDPEventEncoder udpEventEncoder; 20 | 21 | // Create a default pipeline implementation. 22 | private UDPUpstreamHandler upstream; 23 | 24 | public UDPChannelInitializer() 25 | { 26 | 27 | } 28 | 29 | public UDPChannelInitializer(UDPUpstreamHandler upstream) 30 | { 31 | this.upstream = upstream; 32 | } 33 | 34 | @Override 35 | protected void initChannel(DatagramChannel ch) throws Exception { 36 | // pipeline is shared across all channels. 37 | pipeline = ch.pipeline(); 38 | pipeline.addLast("upstream", upstream); 39 | 40 | // Downstream handlers - Filter for data which flows from server to 41 | // client. Note that the last handler added is actually the first 42 | // handler for outgoing data. 43 | // TODO since this is not handling datagram packet will it work out of box? 44 | pipeline.addLast("udpEventEncoder", udpEventEncoder); 45 | 46 | } 47 | 48 | public void setUpstream(UDPUpstreamHandler upstream) 49 | { 50 | this.upstream = upstream; 51 | } 52 | 53 | public UDPUpstreamHandler getUpstream() 54 | { 55 | return upstream; 56 | } 57 | 58 | public UDPEventEncoder getUdpEventEncoder() 59 | { 60 | return udpEventEncoder; 61 | } 62 | 63 | public void setUdpEventEncoder(UDPEventEncoder udpEventEncoder) 64 | { 65 | this.udpEventEncoder = udpEventEncoder; 66 | } 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/app/Game.java: -------------------------------------------------------------------------------- 1 | package io.nadron.app; 2 | 3 | import io.netty.channel.ChannelFuture; 4 | 5 | 6 | 7 | /** 8 | * This interface abstracts a game domain object. Each game deployed in the 9 | * server should implement this interface. 10 | * 11 | * @author Abraham Menacherry 12 | * 13 | */ 14 | public interface Game 15 | { 16 | /** 17 | * @return Returns the unique id associated with this game object. 18 | */ 19 | public Object getId(); 20 | 21 | /** 22 | * @param id 23 | * Sets the unique id for this game. 24 | */ 25 | public void setId(Object id); 26 | 27 | /** 28 | * Get the name of the game. Preferably should be a unique name. 29 | * 30 | * @return Returns the name of the game. 31 | */ 32 | public String getGameName(); 33 | 34 | /** 35 | * Set the name of the game. Preferably it should be a unique value. 36 | * 37 | * @param gameName 38 | * Set the preferably unique game name. 39 | */ 40 | public void setGameName(String gameName); 41 | 42 | /** 43 | * Each game requires a different set of game commands. This method will set 44 | * the interpreter which will convert these commands to method calls. 45 | * 46 | * @return The associated {@link GameCommandInterpreter} instance. 47 | */ 48 | public GameCommandInterpreter getGameCommandInterpreter(); 49 | 50 | /** 51 | * Set the interpreter associated with this game. This method will be used 52 | * if the creation of the interpreter is outside of the implementing game 53 | * room instance, say by a {@link Game} instance or set by the spring 54 | * container. 55 | * 56 | * @param interpreter 57 | * The interpreter instance to set. 58 | */ 59 | public void setGameCommandInterpreter(GameCommandInterpreter interpreter); 60 | 61 | /** 62 | * Unloads the current game, by closing all sessions. This will delegate 63 | * to {@link GameRoom#close()} 64 | * 65 | * @return In case of Netty Implementation it would return a collection of 66 | * {@link ChannelFuture} object. 67 | */ 68 | public Object unload(); 69 | } -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/concurrent/Fibers.java: -------------------------------------------------------------------------------- 1 | package io.nadron.concurrent; 2 | 3 | import java.util.concurrent.ConcurrentHashMap; 4 | import java.util.concurrent.ExecutorService; 5 | import java.util.concurrent.Executors; 6 | 7 | import org.jetlang.fibers.Fiber; 8 | import org.jetlang.fibers.PoolFiberFactory; 9 | import org.jetlang.fibers.ThreadFiber; 10 | 11 | /** 12 | * This class acts as a factory for creating jetlang {@link Fiber}s. 14 | * 15 | * @author Abraham Menacherry 16 | * 17 | */ 18 | public class Fibers 19 | { 20 | // TODO inject this from spring or AppContext 21 | private static final ExecutorService SERVICE; 22 | private static final PoolFiberFactory FACT; 23 | private static final ConcurrentHashMap, PoolFiberFactory> lanePoolFactoryMap = new ConcurrentHashMap, PoolFiberFactory>(); 24 | 25 | static{ 26 | SERVICE = Executors.newSingleThreadExecutor(); 27 | FACT = new PoolFiberFactory(SERVICE); 28 | Runtime.getRuntime().addShutdownHook(new Thread(){ 29 | @Override 30 | public void run() 31 | { 32 | SERVICE.shutdown(); 33 | } 34 | }); 35 | } 36 | 37 | /** 38 | * Creates and starts a fiber and returns the created instance. 39 | * @return The created fiber. 40 | */ 41 | public static Fiber pooledFiber() 42 | { 43 | Fiber fiber = FACT.create(); 44 | fiber.start(); 45 | return fiber; 46 | } 47 | 48 | /** 49 | * Creates and starts a fiber and returns the created instance. 50 | * @return The created fiber. 51 | */ 52 | public static Fiber pooledFiber(Lane lane) 53 | { 54 | if(null == lanePoolFactoryMap.get(lane)) 55 | { 56 | lanePoolFactoryMap.putIfAbsent(lane, new PoolFiberFactory(lane.getUnderlyingLane())); 57 | } 58 | 59 | Fiber fiber = lanePoolFactoryMap.get(lane).create(); 60 | fiber.start(); 61 | return fiber; 62 | } 63 | 64 | public static Fiber threadFiber() 65 | { 66 | Fiber fiber = new ThreadFiber(); 67 | fiber.start(); 68 | return fiber; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /jetclient-as3/src/main/flex/io/nadron/protocol/impl/MessageBufferProtocol.as: -------------------------------------------------------------------------------- 1 | package io.nadron.protocol.impl 2 | { 3 | import flash.net.Socket; 4 | import io.nadron.app.Session; 5 | import io.nadron.codecs.CodecChain; 6 | import io.nadron.codecs.impl.DefaultCodecChain; 7 | import io.nadron.codecs.impl.LengthFieldBasedFrameDecoder; 8 | import io.nadron.codecs.impl.LengthFieldPrepender; 9 | import io.nadron.codecs.impl.MessagBufferEventDecoder; 10 | import io.nadron.codecs.impl.MessageBufferEventEncoder; 11 | import io.nadron.communication.DefaultMessageSender; 12 | import io.nadron.communication.MessageSender; 13 | import io.nadron.protocol.Protocol; 14 | /** 15 | * A default protocol which is of the form . 16 | * @author Abraham Menacherry 17 | */ 18 | public class MessageBufferProtocol implements Protocol 19 | { 20 | public static const name:String = "MessageBufferProtocol"; 21 | private var lengthFieldPrepender:LengthFieldPrepender; 22 | private var messageBufferEventEncoder:MessageBufferEventEncoder; 23 | private var messagBufferEventDecoder:MessagBufferEventDecoder; 24 | 25 | public function MessageBufferProtocol() 26 | { 27 | lengthFieldPrepender = new LengthFieldPrepender(); 28 | messageBufferEventEncoder = new MessageBufferEventEncoder(); 29 | messagBufferEventDecoder = new MessagBufferEventDecoder(); 30 | } 31 | 32 | public function getName():String 33 | { 34 | return name; 35 | } 36 | 37 | public function applyProtocol(session:Session):void 38 | { 39 | var inCodec:CodecChain = new DefaultCodecChain(); 40 | inCodec.add(new LengthFieldBasedFrameDecoder()); 41 | inCodec.add(messagBufferEventDecoder); 42 | session.setInCodecs(inCodec); 43 | } 44 | 45 | public function createMessageSender(socket:Socket):MessageSender 46 | { 47 | var outCodec:CodecChain = new DefaultCodecChain(); 48 | outCodec.add(messageBufferEventEncoder); 49 | outCodec.add(lengthFieldPrepender); 50 | var messageSender:MessageSender = new DefaultMessageSender(socket, outCodec); 51 | return messageSender; 52 | } 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/app/PlayerSession.java: -------------------------------------------------------------------------------- 1 | package io.nadron.app; 2 | 3 | import io.nadron.event.Event; 4 | import io.nadron.protocols.Protocol; 5 | 6 | 7 | /** 8 | * This interface model's a human player's session to nadron. It declares 9 | * methods to get and set the {@link Player}, the {@link GameRoom} to which 10 | * this session will connect and the network {@link Protocol} that will be used 11 | * for communication. 12 | * 13 | * @author Abraham Menacherry 14 | * 15 | */ 16 | public interface PlayerSession extends Session 17 | { 18 | /** 19 | * Each session is associated with a {@link Player}. This is the actual 20 | * human or machine using this session. 21 | * 22 | * @return Returns that associated Player object or null if it is not 23 | * associated yet. 24 | */ 25 | public abstract Player getPlayer(); 26 | 27 | /** 28 | * Each user session is attached to a game room. This method is used to retrieve that 29 | * game room object. 30 | * 31 | * @return Returns the associated game room object or null if none is 32 | * associated. 33 | */ 34 | public abstract GameRoom getGameRoom(); 35 | 36 | /** 37 | * Method used to set the game room for a particular session. 38 | * 39 | * @param gameRoom 40 | * The gameRoom object to set. 41 | */ 42 | public abstract void setGameRoom(GameRoom gameRoom); 43 | 44 | /** 45 | * Get the {@link Protocol} associated with this session. 46 | * 47 | * @return Returns the associated protocol instance. 48 | */ 49 | public Protocol getProtocol(); 50 | 51 | /** 52 | * Set the network protocol on the user session. 53 | * 54 | * @param protocol 55 | * The {@link Protocol} to set. 56 | */ 57 | public void setProtocol(Protocol protocol); 58 | 59 | /** 60 | * The event to be send to the {@link GameRoom} to which the PlayerSession 61 | * belongs. Behavior is unspecified if message is sent when a room change is 62 | * taking place. 63 | * 64 | * @param event The event to send to the {@link GameRoom} 65 | */ 66 | public void sendToGameRoom(Event event); 67 | } 68 | -------------------------------------------------------------------------------- /example-games/src/main/java/io/nadron/example/GameServer.java: -------------------------------------------------------------------------------- 1 | package io.nadron.example; 2 | 3 | import io.nadron.app.GameRoom; 4 | import io.nadron.app.Task; 5 | import io.nadron.example.zombie.domain.World; 6 | import io.nadron.example.zombie.domain.WorldMonitor; 7 | import io.nadron.example.zombie.game.ZombieRoom; 8 | import io.nadron.server.ServerManager; 9 | import io.nadron.service.TaskManagerService; 10 | 11 | import java.util.concurrent.TimeUnit; 12 | 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; 16 | import org.springframework.context.support.AbstractApplicationContext; 17 | 18 | 19 | public class GameServer 20 | { 21 | private static final Logger LOG = LoggerFactory.getLogger(GameServer.class); 22 | 23 | public static void main(String[] args) 24 | { 25 | // Log4j2 auto-configures from log4j2.xml in classpath 26 | AbstractApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); 27 | // For the destroy method to work. 28 | ctx.registerShutdownHook(); 29 | 30 | // Start the main game server 31 | ServerManager serverManager = ctx.getBean(ServerManager.class); 32 | //serverManager.startServers(18090,843,8081); 33 | try 34 | { 35 | serverManager.startServers(); 36 | } 37 | catch (Exception e) 38 | { 39 | LOG.error("Unable to start servers cleanly: {}",e); 40 | } 41 | System.out.println("Started servers"); 42 | startGames(ctx); 43 | } 44 | 45 | public static void startGames(AbstractApplicationContext ctx) 46 | { 47 | // World world = ctx.getBean(World.class); 48 | // GameRoom room1 = (GameRoom)ctx.getBean("Zombie_ROOM_1"); 49 | // GameRoom room2 = (GameRoom)ctx.getBean("Zombie_ROOM_2"); 50 | // Task monitor1 = new WorldMonitor(world,room1); 51 | // Task monitor2 = new WorldMonitor(world,room2); 52 | // TaskManagerService taskManager = ctx.getBean(TaskManagerService.class); 53 | // taskManager.scheduleWithFixedDelay(monitor1, 1000, 5000, TimeUnit.MILLISECONDS); 54 | // taskManager.scheduleWithFixedDelay(monitor2, 2000, 5000, TimeUnit.MILLISECONDS); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/protocols/impl/NettyObjectProtocol.java: -------------------------------------------------------------------------------- 1 | package io.nadron.protocols.impl; 2 | 3 | import io.nadron.app.PlayerSession; 4 | import io.nadron.handlers.netty.DefaultToServerHandler; 5 | import io.nadron.handlers.netty.EventObjectDecoder; 6 | import io.nadron.handlers.netty.EventObjectEncoder; 7 | import io.nadron.protocols.AbstractNettyProtocol; 8 | import io.nadron.util.NettyUtils; 9 | import io.netty.channel.ChannelPipeline; 10 | import io.netty.handler.codec.LengthFieldPrepender; 11 | 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | public class NettyObjectProtocol extends AbstractNettyProtocol { 16 | 17 | private static final Logger LOG = LoggerFactory.getLogger(NettyObjectProtocol.class); 18 | 19 | private LengthFieldPrepender lengthFieldPrepender; 20 | 21 | public NettyObjectProtocol() 22 | { 23 | super("NETTY_OBJECT_PROTOCOL"); 24 | } 25 | 26 | @Override 27 | public void applyProtocol(PlayerSession playerSession) { 28 | LOG.trace("Going to apply {} on session: {}", getProtocolName(), 29 | playerSession); 30 | ChannelPipeline pipeline = NettyUtils.getPipeLineOfConnection(playerSession); 31 | NettyUtils.clearPipeline(pipeline); 32 | 33 | // Upstream handlers or encoders (i.e towards server) are added to 34 | // pipeline now. 35 | pipeline.addLast("lengthDecoder", createLengthBasedFrameDecoder()); 36 | pipeline.addLast("eventDecoder", new EventObjectDecoder()); 37 | pipeline.addLast("eventHandler",new DefaultToServerHandler(playerSession)); 38 | 39 | // Downstream handlers - Filter for data which flows from server to 40 | // client. Note that the last handler added is actually the first 41 | // handler for outgoing data. 42 | pipeline.addLast("lengthFieldPrepender", lengthFieldPrepender); 43 | pipeline.addLast("eventEncoder", new EventObjectEncoder()); 44 | } 45 | 46 | public LengthFieldPrepender getLengthFieldPrepender() 47 | { 48 | return lengthFieldPrepender; 49 | } 50 | 51 | public void setLengthFieldPrepender(LengthFieldPrepender lengthFieldPrepender) 52 | { 53 | this.lengthFieldPrepender = lengthFieldPrepender; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/service/impl/SimpleTaskManagerService.java: -------------------------------------------------------------------------------- 1 | package io.nadron.service.impl; 2 | 3 | import io.nadron.app.Task; 4 | import io.nadron.service.TaskManagerService; 5 | 6 | import java.util.concurrent.ScheduledFuture; 7 | import java.util.concurrent.ScheduledThreadPoolExecutor; 8 | import java.util.concurrent.TimeUnit; 9 | import java.util.concurrent.atomic.AtomicInteger; 10 | 11 | 12 | 13 | /** 14 | * A thin wrapper on a ScheduledThreadPoolExecutor class. It is used so as to keep track of all 16 | * the tasks. In future they could be made durable tasks which can be 17 | * transferred between multiple nodes for fail over, etc. 18 | * 19 | * @author Abraham Menacherry 20 | * 21 | */ 22 | public class SimpleTaskManagerService extends ScheduledThreadPoolExecutor implements 23 | TaskManagerService 24 | { 25 | /** 26 | * Used to create a unique identifier for each task 27 | */ 28 | private AtomicInteger taskNum; 29 | 30 | public SimpleTaskManagerService(int corePoolSize) 31 | { 32 | super(corePoolSize); 33 | taskNum = new AtomicInteger(0); 34 | } 35 | 36 | @Override 37 | public void execute(Task task) 38 | { 39 | super.execute(task); 40 | } 41 | 42 | @Override 43 | @SuppressWarnings("rawtypes") 44 | public ScheduledFuture schedule(final Task task, long delay, TimeUnit unit) 45 | { 46 | task.setId(taskNum.incrementAndGet()); 47 | return super.schedule(task, delay, unit); 48 | } 49 | 50 | @Override 51 | @SuppressWarnings("rawtypes") 52 | public ScheduledFuture scheduleAtFixedRate(Task task, long initialDelay, 53 | long period, TimeUnit unit) 54 | { 55 | task.setId(taskNum.incrementAndGet()); 56 | return super.scheduleAtFixedRate(task, initialDelay, period, unit); 57 | } 58 | 59 | @Override 60 | @SuppressWarnings("rawtypes") 61 | public ScheduledFuture scheduleWithFixedDelay(Task task, 62 | long initialDelay, long delay, TimeUnit unit) 63 | { 64 | task.setId(taskNum.incrementAndGet()); 65 | return super.scheduleWithFixedDelay(task, initialDelay, delay, unit); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/protocols/Protocol.java: -------------------------------------------------------------------------------- 1 | package io.nadron.protocols; 2 | 3 | import io.nadron.app.PlayerSession; 4 | import io.nadron.handlers.netty.LoginHandler; 5 | import io.netty.channel.ChannelPipeline; 6 | 7 | 8 | 9 | /** 10 | * This interface defines a protocol that needs to be applied while 11 | * communicating to the user session object. For the netty implementation, this 12 | * would mean that the protocol would be a series of handlers, decoders and 13 | * encoders added to the pipeline associated with this session to enable the 14 | * relevant protocol. For Example, the STRING protocol would add string encoder 15 | * and decoder to the pipeline. AMF3 would add the relevant serializer and 16 | * de-serializer to the pipeline. 17 | * 18 | * @author Abraham Menacherry 19 | * 20 | */ 21 | public interface Protocol 22 | { 23 | /** 24 | * Return the string name of this protocol. 25 | * 26 | * @return name of the protocol. This will be used in scenarios where a 27 | * custom protocol has been used. 28 | */ 29 | public String getProtocolName(); 30 | 31 | /** 32 | * The main method of this interface. For the Netty implementation, this 33 | * will be used to add the handlers in the pipeline associated with this 34 | * user session. For now, "configuration" only means adding of handlers. 35 | * It is expected that the {@link LoginHandler} or whichever previous 36 | * handler was handling the message has cleared up the 37 | * {@link ChannelPipeline} object. 38 | * 39 | * @param playerSession 40 | * The user session for which the protocol handlers need to be 41 | * set. 42 | */ 43 | public void applyProtocol(PlayerSession playerSession); 44 | 45 | /** 46 | * This method delegates to the {@link #applyProtocol(PlayerSession)} method 47 | * after clearing the pipeline based on the input flag. 48 | * 49 | * @param playerSession 50 | * @param clearExistingProtocolHandlers 51 | * Clears the pipeline of existing protocol handlers if set to 52 | * true. 53 | */ 54 | public void applyProtocol(PlayerSession playerSession, 55 | boolean clearExistingProtocolHandlers); 56 | } 57 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/config/ServiceBeansConfig.java: -------------------------------------------------------------------------------- 1 | package io.nadron.config; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import io.nadron.service.impl.ReconnectSessionRegistry; 5 | import io.nadron.service.impl.SessionRegistry; 6 | import io.nadron.service.impl.SimpleGameAdminService; 7 | import io.nadron.service.impl.SimpleTaskManagerService; 8 | import io.nadron.service.impl.SimpleUniqueIdGenerator; 9 | import org.msgpack.MessagePack; 10 | import org.springframework.beans.factory.annotation.Value; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | import org.springframework.context.annotation.PropertySource; 14 | 15 | import java.util.HashSet; 16 | 17 | /** 18 | * Spring Java configuration replacing service-beans.xml 19 | */ 20 | @Configuration 21 | @PropertySource("classpath:nadron/props/nadron.properties") 22 | public class ServiceBeansConfig { 23 | 24 | @Value("${reconnect.delay}") 25 | private long reconnectDelay; 26 | 27 | @Bean 28 | public SimpleTaskManagerService taskManagerService() { 29 | return new SimpleTaskManagerService(2); 30 | } 31 | 32 | @Bean 33 | public SimpleGameAdminService gameAdminService() { 34 | SimpleGameAdminService service = new SimpleGameAdminService(); 35 | service.setGames(new HashSet<>()); 36 | return service; 37 | } 38 | 39 | @Bean 40 | public SessionRegistry udpSessionRegistry() { 41 | return new SessionRegistry(); 42 | } 43 | 44 | @Bean 45 | public ReconnectSessionRegistry reconnectSessionRegistry() { 46 | ReconnectSessionRegistry registry = new ReconnectSessionRegistry(); 47 | registry.setTaskManagerService(taskManagerService()); 48 | registry.setReconnectDelay((int) reconnectDelay); 49 | return registry; 50 | } 51 | 52 | @Bean 53 | public SimpleUniqueIdGenerator simpleUniqueIdGenerator() { 54 | return new SimpleUniqueIdGenerator(); 55 | } 56 | 57 | @Bean 58 | public ObjectMapper jackson() { 59 | return new ObjectMapper(); 60 | } 61 | 62 | @Bean 63 | public MessagePack msgPack() { 64 | return new MessagePack(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/handlers/netty/DefaultToClientHandler.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.handlers.netty; 2 | 3 | import io.nadron.client.NettyTCPClient; 4 | import io.nadron.client.app.Session; 5 | import io.nadron.client.event.Event; 6 | import io.nadron.client.event.Events; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import io.netty.channel.ChannelPipeline; 9 | import io.netty.channel.SimpleChannelInboundHandler; 10 | import io.netty.channel.ChannelHandler.Sharable; 11 | 12 | 13 | /** 14 | * A stateful handler whose job is to transmit messages coming on the Netty 15 | * {@link ChannelPipeline} to the session. 16 | * 17 | * @author Abraham Menacherry. 18 | * 19 | */ 20 | @Sharable 21 | public class DefaultToClientHandler extends SimpleChannelInboundHandler 22 | { 23 | static final String NAME = "defaultHandler"; 24 | private final Session session; 25 | 26 | public DefaultToClientHandler(Session session) 27 | { 28 | this.session = session; 29 | } 30 | 31 | @Override 32 | public void channelRead0(ChannelHandlerContext ctx, 33 | Event event) throws Exception 34 | { 35 | session.onEvent(event); 36 | } 37 | 38 | // TODO check what other methods need to be caught. 39 | 40 | @Override 41 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 42 | NettyTCPClient.ALL_CHANNELS.add(ctx.channel()); 43 | super.channelActive(ctx); 44 | } 45 | 46 | @Override 47 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) 48 | throws Exception 49 | { 50 | System.err.println("Class:DefaultToClientHandler" 51 | + " Exception occurred in tcp channel: " + cause); 52 | Event event = Events.event(cause, Events.EXCEPTION); 53 | session.onEvent(event); 54 | } 55 | 56 | // TODO see if this causes reconnection failure 57 | @Override 58 | public void channelInactive(ChannelHandlerContext ctx) 59 | throws Exception 60 | { 61 | if (!session.isShuttingDown()) 62 | { 63 | // Should not send close to session, since reconnection/other 64 | // business logic might be in place. 65 | Event event = Events.event(null, Events.DISCONNECT); 66 | session.onEvent(event); 67 | } 68 | } 69 | 70 | public static String getName() 71 | { 72 | return NAME; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/event/EventDispatcher.java: -------------------------------------------------------------------------------- 1 | package io.nadron.event; 2 | 3 | import io.nadron.app.Session; 4 | 5 | import java.util.List; 6 | 7 | 8 | 9 | /** 10 | * EventDispatcher's are associated with a session, so that the session can use 11 | * it to dispatch incoming events to the appropriate handlers. 12 | * 13 | * @author Abraham Menacherry 14 | * 15 | */ 16 | public interface EventDispatcher 17 | { 18 | /** 19 | * Adds event handler to the dispatcher. Using this method, different events 20 | * can be handled using different handlers. 21 | * 22 | * @param eventHandler The event handler to be added to the dispatcher. 23 | */ 24 | public void addHandler( EventHandler eventHandler ); 25 | 26 | /** 27 | * Returns the list of {@link EventHandler}s associated with a particular 28 | * event type. 29 | * 30 | * @param eventType 31 | * The type of event. 32 | * @return The list {@link EventHandler}s associated with that event or 33 | * null. 34 | */ 35 | public List getHandlers(int eventType); 36 | 37 | /** 38 | * Removes an event handler from the dispatcher 39 | * 40 | * @param eventHandler 41 | * the event handler to be removed from the dispatcher 42 | */ 43 | public void removeHandler(EventHandler eventHandler); 44 | 45 | /** 46 | * Removes all event listeners associated with the event type. 47 | */ 48 | public void removeHandlersForEvent(int eventType); 49 | 50 | /** 51 | * Removes all the handlers for a session. 52 | * 53 | * @param session 54 | * The session instance from which event handlers need to be 55 | * removed. 56 | * @return Returns true if all handlers were successfully removed. 57 | */ 58 | boolean removeHandlersForSession(Session session); 59 | 60 | /** 61 | * Clears all handles associated with this dispatcher. 62 | * 63 | */ 64 | void clear(); 65 | 66 | /** 67 | * Fires event in asynchronous mode 68 | * 69 | */ 70 | public void fireEvent( Event event ); 71 | 72 | /** 73 | * Called by the session during disconnect, the dispatcher will no longer 74 | * accept any events, it will also detach the existing listeners. 75 | */ 76 | public void close(); 77 | 78 | } 79 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/event/EventDispatcher.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.event; 2 | 3 | import io.nadron.client.app.Session; 4 | 5 | import java.util.List; 6 | 7 | 8 | /** 9 | * Event Dispatchers are used by {@link Session} to dispatch the incoming event 10 | * on its {@link Session#onEvent(Event)} method to the correct 11 | * {@link EventHandler}. This interface has methods for adding removing such 12 | * handlers. 13 | * 14 | * @author Abraham Menacherry 15 | * 16 | */ 17 | public interface EventDispatcher 18 | { 19 | /** 20 | * Adds event handler to the dispatcher. Using this method, different events 21 | * can be handled using different handlers. 22 | * 23 | * @param eventHandler 24 | * The event handler to be added to the dispatcher. 25 | */ 26 | void addHandler(EventHandler eventHandler); 27 | 28 | /** 29 | * Returns the list of {@link EventHandler}s associated with a particular 30 | * event type. 31 | * 32 | * @param eventType 33 | * The type of event. 34 | * @return The list {@link EventHandler}s associated with that event or 35 | * null. 36 | */ 37 | List getHandlers(int eventType); 38 | 39 | /** 40 | * Removes an event handler from the dispatcher 41 | * 42 | * @param eventHandler 43 | * the event handler to be removed from the dispatcher 44 | */ 45 | void removeHandler(EventHandler eventHandler); 46 | 47 | /** 48 | * Removes all event listeners associated with the event type. 49 | */ 50 | void removeHandlersForEvent(int eventType); 51 | 52 | /** 53 | * Removes all the handlers for a session. 54 | * 55 | * @param session 56 | * @return Returns true if all handlers were successfully removed. 57 | */ 58 | boolean removeHandlersForSession(Session session); 59 | 60 | /** 61 | * Clears all handles associated with this dispatcher and returns the number 62 | * of handlers cleared. 63 | * 64 | */ 65 | void clear(); 66 | 67 | /** 68 | * Fires event in asynchronous mode 69 | * 70 | * @param event 71 | */ 72 | void fireEvent(Event event); 73 | 74 | /** 75 | * Called by the session during disconnect, the dispatcher will no longer 76 | * accept any events, it will also detach the existing listeners. 77 | */ 78 | void close(); 79 | 80 | } 81 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/convert/flex/AMFSerializer.java: -------------------------------------------------------------------------------- 1 | package io.nadron.convert.flex; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | 6 | import flex.messaging.io.SerializationContext; 7 | import flex.messaging.io.amf.Amf3Output; 8 | 9 | /** 10 | * This class is used to serialize java objects to AMF3 binary format. It uses 11 | * AMF3 output class from blazeds library for this purpose. 12 | * 13 | * @author Abraham Menacherry 14 | * 15 | */ 16 | public class AMFSerializer 17 | { 18 | /** 19 | * Used by the blazeds api for its context. 20 | */ 21 | private SerializationContext context; 22 | 23 | /** 24 | * This constructor is used by the PictureDataEncoder class in order to 25 | * create an instance of this class and later use it for converting the java 26 | * object to AMF3. 27 | * 28 | * @param serializationContext 29 | */ 30 | public AMFSerializer(SerializationContext serializationContext) 31 | { 32 | this.context = serializationContext; 33 | } 34 | 35 | /** 36 | * Method used to convert the java object to AMF3 format. This method in 37 | * turn delegates this conversion to the blazeds class AMF3 output. 38 | * 39 | * @param 40 | * @param source 41 | * This is the java object that is to be converted to AMF3. 42 | * @return Returns the byte array output stream which was created by 43 | * serializing a java object to AMF3. 44 | * @throws IOException 45 | */ 46 | public ByteArrayOutputStream toAmf(final T source) throws IOException 47 | { 48 | // Create and instance of the byte array output stream to write the AMF3 49 | // object to. 50 | final ByteArrayOutputStream bout = new ByteArrayOutputStream(); 51 | // Create the AMF3Output object and set its output stream. 52 | final Amf3Output amf3Output = new Amf3Output(context); 53 | amf3Output.setOutputStream(bout); 54 | // Write the AMF3 object to the byte array output stream after 55 | // converting the source java object. 56 | amf3Output.writeObject(source); 57 | // Flush and close the stream. 58 | amf3Output.flush(); 59 | amf3Output.close(); 60 | return bout; 61 | } 62 | 63 | public SerializationContext getContext() 64 | { 65 | return context; 66 | } 67 | 68 | public void setContext(SerializationContext context) 69 | { 70 | this.context = context; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/handlers/netty/MessageBufferEventEncoder.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.handlers.netty; 2 | 3 | import io.nadron.client.communication.MessageBuffer; 4 | import io.nadron.client.event.Event; 5 | import io.nadron.client.event.Events; 6 | import io.netty.buffer.ByteBuf; 7 | import io.netty.channel.ChannelHandler.Sharable; 8 | import io.netty.channel.ChannelHandlerContext; 9 | import io.netty.handler.codec.MessageToMessageEncoder; 10 | 11 | import java.util.List; 12 | 13 | 14 | /** 15 | * Converts an incoming {@link Event} which in turn has a 16 | * IMessageBuffer payload to a Netty {@link ByteBuf}. 17 | * Note that {@link Event} instances containing other type of objects as its 18 | * payload will result in {@link ClassCastException}. 19 | * 20 | * @author Abraham Menacherry. 21 | * 22 | */ 23 | // TODO check if MessageToMessageEncoder can be replaced with MessageToByteEncoder 24 | @Sharable 25 | public class MessageBufferEventEncoder extends MessageToMessageEncoder 26 | { 27 | 28 | @Override 29 | protected void encode(ChannelHandlerContext ctx, Event event, 30 | List out) throws Exception 31 | { 32 | out.add(encode(ctx, event)); 33 | } 34 | 35 | /** 36 | * Encode is separated out so that child classes can still reuse this 37 | * functionality. 38 | * 39 | * @param ctx 40 | * @param event 41 | * The event to be encoded into {@link ByteBuf}. It will be 42 | * converted to 'opcode'-'payload' format. 43 | * @return If only opcode is specified a single byte {@link ByteBuf} is 44 | * returned, otherwise a byte buf with 'opcode'-'payload' format is 45 | * returned. 46 | */ 47 | protected ByteBuf encode(ChannelHandlerContext ctx, Event event) 48 | { 49 | ByteBuf out = ctx.alloc().buffer(); 50 | out.writeByte(event.getType()); 51 | if (Events.LOG_IN == event.getType() || Events.RECONNECT == event.getType()) 52 | { 53 | // write protocol version also 54 | out.writeByte(Events.PROTOCOL_VERSION); 55 | } 56 | 57 | if (null != event.getSource()) 58 | { 59 | @SuppressWarnings("unchecked") 60 | MessageBuffer msgBuffer = (MessageBuffer) event 61 | .getSource(); 62 | ByteBuf data = msgBuffer.getNativeBuffer(); 63 | out.writeBytes(data); 64 | data.release(); 65 | } 66 | return out; 67 | } 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/server/netty/ServerManagerImpl.java: -------------------------------------------------------------------------------- 1 | package io.nadron.server.netty; 2 | 3 | import io.nadron.context.AppContext; 4 | import io.nadron.server.ServerManager; 5 | 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | 13 | public class ServerManagerImpl implements ServerManager 14 | { 15 | private Set servers; 16 | private static final Logger LOG = LoggerFactory.getLogger(ServerManagerImpl.class); 17 | 18 | public ServerManagerImpl() 19 | { 20 | servers = new HashSet(); 21 | } 22 | 23 | @Override 24 | public void startServers(int tcpPort, int flashPort, int udpPort) throws Exception 25 | { 26 | 27 | if(tcpPort > 0) 28 | { 29 | AbstractNettyServer tcpServer = (AbstractNettyServer)AppContext.getBean(AppContext.TCP_SERVER); 30 | tcpServer.startServer(tcpPort); 31 | servers.add(tcpServer); 32 | } 33 | 34 | if(flashPort > 0) 35 | { 36 | AbstractNettyServer flashServer = (AbstractNettyServer)AppContext.getBean(AppContext.FLASH_POLICY_SERVER); 37 | flashServer.startServer(flashPort); 38 | servers.add(flashServer); 39 | } 40 | 41 | if(udpPort > 0) 42 | { 43 | AbstractNettyServer udpServer = (AbstractNettyServer)AppContext.getBean(AppContext.UDP_SERVER); 44 | udpServer.startServer(udpPort); 45 | servers.add(udpServer); 46 | } 47 | 48 | } 49 | 50 | @Override 51 | public void startServers() throws Exception 52 | { 53 | AbstractNettyServer tcpServer = (AbstractNettyServer)AppContext.getBean(AppContext.TCP_SERVER); 54 | tcpServer.startServer(); 55 | servers.add(tcpServer); 56 | AbstractNettyServer flashServer = (AbstractNettyServer)AppContext.getBean(AppContext.FLASH_POLICY_SERVER); 57 | flashServer.startServer(); 58 | servers.add(flashServer); 59 | AbstractNettyServer udpServer = (AbstractNettyServer)AppContext.getBean(AppContext.UDP_SERVER); 60 | udpServer.startServer(); 61 | servers.add(udpServer); 62 | } 63 | 64 | @Override 65 | public void stopServers() throws Exception 66 | { 67 | for(AbstractNettyServer nettyServer: servers){ 68 | try 69 | { 70 | nettyServer.stopServer(); 71 | } 72 | catch (Exception e) 73 | { 74 | LOG.error("Unable to stop server {} due to error {}", nettyServer,e); 75 | throw e; 76 | } 77 | } 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /jetclient-as3/src/main/flex/io/nadron/communication/MessageBuffer.as: -------------------------------------------------------------------------------- 1 | package io.nadron.communication 2 | { 3 | import flash.utils.ByteArray; 4 | 5 | /** 6 | * Not completed yet! A thin wrapper over ByteArray which will provide utility methods for writing and reading multiple Strings. 7 | * @author Abraham Menacherry 8 | */ 9 | public class MessageBuffer 10 | { 11 | private var buffer:ByteArray; 12 | 13 | public function MessageBuffer(byteArray:ByteArray) 14 | { 15 | if (null == byteArray) { 16 | buffer = new ByteArray(); 17 | } else{ 18 | buffer = byteArray; 19 | } 20 | } 21 | 22 | /** 23 | * Writes multiple Strings to the underlying ByteArray. Each string is written as 24 | * to the array since JetServer protocol expects it in this way. 25 | * @param ... args 26 | */ 27 | public function writeMultiStrings(... args):void { 28 | for (var i:int = 0; i < args.length; i++) { 29 | writeString(args[i]) 30 | } 31 | } 32 | 33 | /** 34 | * Each string is written as to the array since Nadron Server protocol expects it in this way 35 | * @param theString 36 | */ 37 | public function writeString(theString:String):void { 38 | var bytes:ByteArray = new ByteArray(); 39 | bytes.writeUTFBytes(theString); 40 | return writeBytes(bytes); 41 | } 42 | 43 | /** 44 | * Reads the length and then the actual string of that length from the underlying buffer. 45 | * @return 46 | */ 47 | public function readString():String { 48 | var utfBytes:ByteArray = readBytes(); 49 | var str:String = null; 50 | if (utfBytes != null) { 51 | str = utfBytes.readUTF(); 52 | } 53 | return str; 54 | } 55 | 56 | /** 57 | * Writes bytes to the underlying buffer, with the length of the bytes prepended. 58 | * @param bytes 59 | */ 60 | public function writeBytes(bytes:ByteArray):void { 61 | buffer.writeShort(bytes.length); 62 | buffer.writeBytes(bytes); 63 | } 64 | 65 | public function readBytes():ByteArray { 66 | var length:int = buffer.readShort(); 67 | var bytes:ByteArray = null; 68 | if ((length > 0) && buffer.bytesAvailable >= length) { 69 | bytes = new ByteArray(); 70 | buffer.readBytes(bytes); 71 | } 72 | return bytes; 73 | } 74 | 75 | public function getBuffer():ByteArray { 76 | return buffer; 77 | } 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /nadron/src/main/java/io/nadron/communication/NettyTCPMessageSender.java: -------------------------------------------------------------------------------- 1 | package io.nadron.communication; 2 | 3 | import io.nadron.communication.DeliveryGuaranty.DeliveryGuarantyOptions; 4 | import io.nadron.communication.MessageSender.Reliable; 5 | import io.nadron.event.Event; 6 | import io.nadron.event.Events; 7 | import io.netty.channel.Channel; 8 | import io.netty.channel.ChannelFutureListener; 9 | 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | /** 14 | * A class that transmits messages reliably to remote machines/vm's. Internally 15 | * this class uses Netty tcp {@link Channel} to transmit the message. 16 | * 17 | * @author Abraham Menacherry 18 | * 19 | */ 20 | public class NettyTCPMessageSender implements Reliable 21 | { 22 | private final Channel channel; 23 | private static final DeliveryGuaranty DELIVERY_GUARANTY = DeliveryGuarantyOptions.RELIABLE; 24 | private static final Logger LOG = LoggerFactory 25 | .getLogger(NettyTCPMessageSender.class); 26 | 27 | public NettyTCPMessageSender(Channel channel) 28 | { 29 | super(); 30 | this.channel = channel; 31 | } 32 | 33 | @Override 34 | public Object sendMessage(Object message) 35 | { 36 | return channel.writeAndFlush(message); 37 | } 38 | 39 | @Override 40 | public DeliveryGuaranty getDeliveryGuaranty() 41 | { 42 | return DELIVERY_GUARANTY; 43 | } 44 | 45 | public Channel getChannel() 46 | { 47 | return channel; 48 | } 49 | 50 | /** 51 | * Writes an the {@link Events#DISCONNECT} to the client, flushes 52 | * all the pending writes and closes the channel. 53 | * 54 | */ 55 | @Override 56 | public void close() 57 | { 58 | LOG.debug("Going to close tcp connection in class: {}", this 59 | .getClass().getName()); 60 | Event event = Events.event(null, Events.DISCONNECT); 61 | if (channel.isActive()) 62 | { 63 | channel.write(event).addListener(ChannelFutureListener.CLOSE); 64 | } 65 | else 66 | { 67 | channel.close(); 68 | LOG.trace("Unable to write the Event {} with type {} to socket", 69 | event, event.getType()); 70 | } 71 | } 72 | 73 | @Override 74 | public String toString() 75 | { 76 | String channelId = "TCP channel: "; 77 | if (null != channel) 78 | { 79 | channelId += channel.toString(); 80 | } 81 | else 82 | { 83 | channelId += "0"; 84 | } 85 | String sender = "Netty " + channelId; 86 | return sender; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /nadclient/src/main/java/io/nadron/client/communication/NettyUDPMessageSender.java: -------------------------------------------------------------------------------- 1 | package io.nadron.client.communication; 2 | 3 | import io.nadron.client.NettyUDPClient; 4 | import io.nadron.client.app.Session; 5 | import io.nadron.client.communication.MessageSender.Fast; 6 | import io.nadron.client.event.Events; 7 | import io.netty.channel.socket.DatagramChannel; 8 | 9 | import java.net.SocketAddress; 10 | 11 | 12 | /** 13 | * This class is used to send messages to a remote UDP client or server. An 14 | * instance of this class will be created when the {@link Events#CONNECT} event 15 | * is sent to a {@link Session} 16 | * 17 | * @author Abraham Menacherry 18 | * 19 | */ 20 | public class NettyUDPMessageSender implements Fast 21 | { 22 | private boolean isClosed = false; 23 | private final SocketAddress remoteAddress; 24 | private final DatagramChannel channel; 25 | private static final DeliveryGuaranty DELIVERY_GUARANTY = DeliveryGuaranty.DeliveryGuarantyOptions.FAST; 26 | 27 | public NettyUDPMessageSender(SocketAddress remoteAddress, 28 | DatagramChannel channel) 29 | { 30 | this.remoteAddress = remoteAddress; 31 | this.channel = channel; 32 | } 33 | 34 | @Override 35 | public Object sendMessage(Object message) 36 | { 37 | return channel.write(message); 38 | } 39 | 40 | @Override 41 | public DeliveryGuaranty getDeliveryGuaranty() 42 | { 43 | return DELIVERY_GUARANTY; 44 | } 45 | 46 | @Override 47 | public synchronized void close() 48 | { 49 | if (isClosed) 50 | return; 51 | Session session = NettyUDPClient.CLIENTS.remove(channel.localAddress()); 52 | if (null == session) 53 | { 54 | System.err.println("Possible memory leak occurred. " 55 | + "The session associated with udp localaddress: " 56 | + channel.localAddress() 57 | + " could not be removed from NettyUDPClient.CLIENTS map"); 58 | } 59 | isClosed = true; 60 | } 61 | 62 | public SocketAddress getRemoteAddress() 63 | { 64 | return remoteAddress; 65 | } 66 | 67 | public DatagramChannel getChannel() 68 | { 69 | return channel; 70 | } 71 | 72 | @Override 73 | public String toString() 74 | { 75 | String channelId = "UDP Channel: "; 76 | if (null != channel) 77 | { 78 | channelId += channel.toString(); 79 | } 80 | else 81 | { 82 | channelId += "0"; 83 | } 84 | String sender = "Netty " + channelId + " RemoteAddress: " 85 | + remoteAddress; 86 | return sender; 87 | } 88 | } 89 | --------------------------------------------------------------------------------