Skip to content

Commit 668694e

Browse files
committed
Added the basic game session services for client and server communication.
Right now the client isn't calling anything as that starts to get into real "game" stuff but the move() method is there and hooked up. The server calls back to the clients to notify them about other players leaving and joining.
1 parent 3546c0b commit 668694e

10 files changed

+616
-12
lines changed

sim-eth-basic/src/main/java/example/ConnectionState.java

+5
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import com.jme3.network.ClientStateListener;
5151
import com.jme3.network.ClientStateListener.DisconnectInfo;
5252
import com.jme3.network.ErrorListener;
53+
import com.jme3.network.service.ClientService;
5354

5455
import com.simsilica.lemur.Action;
5556
import com.simsilica.lemur.Button;
@@ -90,6 +91,10 @@ public ConnectionState( AppState parent, String host, int port ) {
9091
this.port = port;
9192
}
9293

94+
public <T extends ClientService> T getService( Class<T> type ) {
95+
return client.getService(type);
96+
}
97+
9398
public void disconnect() {
9499
log.info("disconnect()");
95100
closing = true;

sim-eth-basic/src/main/java/example/GameSessionState.java

+22
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@
4444
import com.simsilica.lemur.input.InputMapper;
4545
import com.simsilica.state.CompositeAppState;
4646

47+
import example.net.GameSessionListener;
48+
import example.net.client.GameSessionClientService;
49+
4750
/**
4851
* The core state that manages the game session. This has several
4952
* child app states whose lifecycles are directly linked to this one.
@@ -52,6 +55,8 @@
5255
*/
5356
public class GameSessionState extends CompositeAppState {
5457

58+
private GameSessionObserver gameSessionObserver = new GameSessionObserver();
59+
5560
public GameSessionState() {
5661
// add normal states on the super-constructor
5762
super(new MessageState());
@@ -72,8 +77,12 @@ protected void initialize( Application app ) {
7277

7378
EventBus.publish(GameSessionEvent.sessionStarted, new GameSessionEvent());
7479

80+
// Add a self-message because we're too late to have caught the
81+
// player joined message for ourselves. (Please we'd want it to look like this, anyway.)
7582
getState(MessageState.class).addMessage("> You have joined the game.", ColorRGBA.Yellow);
7683

84+
getState(ConnectionState.class).getService(GameSessionClientService.class).addGameSessionListener(gameSessionObserver);
85+
7786
InputMapper inputMapper = GuiGlobals.getInstance().getInputMapper();
7887
inputMapper.activateGroup(MainGameFunctions.IN_GAME);
7988
}
@@ -92,4 +101,17 @@ protected void cleanup( Application app ) {
92101

93102
super.cleanup(app);
94103
}
104+
105+
/**
106+
* Notified by the server about game-session related events.
107+
*/
108+
private class GameSessionObserver implements GameSessionListener {
109+
public void playerJoined( int clientId, String playerName ){
110+
getState(MessageState.class).addMessage("> " + playerName + " has joined.", ColorRGBA.Yellow);
111+
}
112+
113+
public void playerLeft( int clientId, String playerName ) {
114+
getState(MessageState.class).addMessage("> " + playerName + " has left.", ColorRGBA.Yellow);
115+
}
116+
}
95117
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* $Id$
3+
*
4+
* Copyright (c) 2016, Simsilica, LLC
5+
* All rights reserved.
6+
*
7+
* Redistribution and use in source and binary forms, with or without
8+
* modification, are permitted provided that the following conditions
9+
* are met:
10+
*
11+
* 1. Redistributions of source code must retain the above copyright
12+
* notice, this list of conditions and the following disclaimer.
13+
*
14+
* 2. Redistributions in binary form must reproduce the above copyright
15+
* notice, this list of conditions and the following disclaimer in
16+
* the documentation and/or other materials provided with the
17+
* distribution.
18+
*
19+
* 3. Neither the name of the copyright holder nor the names of its
20+
* contributors may be used to endorse or promote products derived
21+
* from this software without specific prior written permission.
22+
*
23+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26+
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27+
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
28+
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31+
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32+
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
34+
* OF THE POSSIBILITY OF SUCH DAMAGE.
35+
*/
36+
37+
package example.net;
38+
39+
import com.jme3.math.Quaternion;
40+
import com.jme3.network.service.rmi.Asynchronous;
41+
42+
43+
/**
44+
* The client's view of the 'game'. Provides necessary access to the
45+
* general game interaction and possibly game or player state.
46+
*
47+
* @author Paul Speed
48+
*/
49+
public interface GameSession {
50+
51+
/**
52+
* Sends information to the game back end about the current
53+
* movement state of the player from user input. Because this
54+
* state is continuous, it doesn't need to be reliable.
55+
*/
56+
@Asynchronous(reliable=false)
57+
public void move( Quaternion rotation, float speed );
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* $Id$
3+
*
4+
* Copyright (c) 2016, Simsilica, LLC
5+
* All rights reserved.
6+
*
7+
* Redistribution and use in source and binary forms, with or without
8+
* modification, are permitted provided that the following conditions
9+
* are met:
10+
*
11+
* 1. Redistributions of source code must retain the above copyright
12+
* notice, this list of conditions and the following disclaimer.
13+
*
14+
* 2. Redistributions in binary form must reproduce the above copyright
15+
* notice, this list of conditions and the following disclaimer in
16+
* the documentation and/or other materials provided with the
17+
* distribution.
18+
*
19+
* 3. Neither the name of the copyright holder nor the names of its
20+
* contributors may be used to endorse or promote products derived
21+
* from this software without specific prior written permission.
22+
*
23+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26+
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27+
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
28+
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31+
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32+
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
34+
* OF THE POSSIBILITY OF SUCH DAMAGE.
35+
*/
36+
37+
package example.net;
38+
39+
import com.jme3.network.service.rmi.Asynchronous;
40+
41+
/**
42+
* The asynchronous callbacks that the game back-end uses to
43+
* send game-session information to the player.
44+
*
45+
* @author Paul Speed
46+
*/
47+
public interface GameSessionListener {
48+
49+
/**
50+
* Called when a new player has joined the game.
51+
*/
52+
@Asynchronous
53+
public void playerJoined( int clientId, String playerName );
54+
55+
/**
56+
* Called when an existing player has left the game.
57+
*/
58+
@Asynchronous
59+
public void playerLeft( int clientId, String playerName );
60+
}

sim-eth-basic/src/main/java/example/net/client/GameClient.java

+2-7
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,8 @@ public GameClient( String host, int port ) throws IOException {
6767
log.info("Adding services...");
6868
client.getServices().addServices(new RpcClientService(),
6969
new RmiClientService(),
70-
new AccountClientService()//,
71-
//new EntityClientService(),
72-
//new ArenaClient(),
73-
//new ChatClient((short)-2),
74-
//new EtherealClient(ESpaceConstants.OBJECT_PROTOCOL,
75-
// ESpaceConstants.ZONE_GRID,
76-
// ESpaceConstants.ZONE_RADIUS));
70+
new AccountClientService(),
71+
new GameSessionClientService()
7772
);
7873
}
7974

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/*
2+
* $Id$
3+
*
4+
* Copyright (c) 2016, Simsilica, LLC
5+
* All rights reserved.
6+
*
7+
* Redistribution and use in source and binary forms, with or without
8+
* modification, are permitted provided that the following conditions
9+
* are met:
10+
*
11+
* 1. Redistributions of source code must retain the above copyright
12+
* notice, this list of conditions and the following disclaimer.
13+
*
14+
* 2. Redistributions in binary form must reproduce the above copyright
15+
* notice, this list of conditions and the following disclaimer in
16+
* the documentation and/or other materials provided with the
17+
* distribution.
18+
*
19+
* 3. Neither the name of the copyright holder nor the names of its
20+
* contributors may be used to endorse or promote products derived
21+
* from this software without specific prior written permission.
22+
*
23+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26+
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27+
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
28+
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31+
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32+
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
34+
* OF THE POSSIBILITY OF SUCH DAMAGE.
35+
*/
36+
37+
package example.net.client;
38+
39+
import java.util.List;
40+
import java.util.concurrent.CopyOnWriteArrayList;
41+
42+
import org.slf4j.*;
43+
44+
import com.jme3.math.Quaternion;
45+
import com.jme3.network.service.AbstractClientService;
46+
import com.jme3.network.service.ClientServiceManager;
47+
import com.jme3.network.service.rmi.RmiClientService;
48+
49+
import example.net.GameSession;
50+
import example.net.GameSessionListener;
51+
52+
/**
53+
* Manages the client-side hook-up for the GameSession.
54+
*
55+
* @author Paul Speed
56+
*/
57+
public class GameSessionClientService extends AbstractClientService
58+
implements GameSession {
59+
60+
static Logger log = LoggerFactory.getLogger(GameSessionClientService.class);
61+
62+
private RmiClientService rmiService;
63+
private AccountClientService accountService;
64+
private GameSession delegate;
65+
66+
private GameSessionCallback sessionCallback = new GameSessionCallback();
67+
private List<GameSessionListener> listeners = new CopyOnWriteArrayList<>();
68+
69+
public GameSessionClientService() {
70+
}
71+
72+
@Override
73+
public void move( Quaternion dir, float speed ) {
74+
log.info("move(" + dir + ", " + speed + ")");
75+
delegate.move(dir, speed);
76+
}
77+
78+
private GameSession getDelegate() {
79+
// We look up the delegate lazily to make the service more
80+
// flexible. Otherwise we'd have to listen to the account service
81+
// to know when we'd fully logged on and that creates an unnecessary
82+
// dependency for a relatively small thing. Easier just to lazily
83+
// load it upon request and hope the client is already handling the
84+
// state properly.
85+
if( delegate == null ) {
86+
// Look it up
87+
this.delegate = rmiService.getRemoteObject(GameSession.class);
88+
log.debug("delegate:" + delegate);
89+
if( delegate == null ) {
90+
throw new RuntimeException("No game session found");
91+
}
92+
}
93+
return delegate;
94+
}
95+
96+
/**
97+
* Adds a listener that will be notified about account-related events.
98+
* Note that these listeners are called on the networking thread and
99+
* as such are not suitable for modifying the visualization directly.
100+
*/
101+
public void addGameSessionListener( GameSessionListener l ) {
102+
listeners.add(l);
103+
}
104+
105+
public void removeGameSessionListener( GameSessionListener l ) {
106+
listeners.remove(l);
107+
}
108+
109+
@Override
110+
protected void onInitialize( ClientServiceManager s ) {
111+
log.debug("onInitialize(" + s + ")");
112+
this.rmiService = getService(RmiClientService.class);
113+
if( rmiService == null ) {
114+
throw new RuntimeException("GameSessionClientService requires RMI service");
115+
}
116+
117+
// Register the session right away even though the 'state' of the connection
118+
// is that we are not actually in the game yet. Because the server is managing
119+
// that state, it does no harm for us to register the callback early and this
120+
// way we avoid any case where the server might try to call it before we are
121+
// fully ready. (ie: it's friendlier to async messaging)
122+
log.debug("Sharing session callback.");
123+
rmiService.share(sessionCallback, GameSessionListener.class);
124+
}
125+
126+
/**
127+
* Called during connection setup once the server-side services have been initialized
128+
* for this connection and any shared objects, etc. should be available.
129+
*/
130+
@Override
131+
public void start() {
132+
log.debug("start()");
133+
super.start();
134+
}
135+
136+
/**
137+
* Shared with the server over RMI so that it can notify us about account
138+
* related stuff.
139+
*/
140+
private class GameSessionCallback implements GameSessionListener {
141+
142+
@Override
143+
public void playerJoined( int clientId, String playerName ) {
144+
log.debug("playerJoined(" + clientId + ", " + playerName + ")");
145+
for( GameSessionListener l : listeners ) {
146+
l.playerJoined(clientId, playerName);
147+
}
148+
}
149+
150+
@Override
151+
public void playerLeft( int clientId, String playerName ) {
152+
log.debug("playerLeft(" + clientId + ", " + playerName + ")");
153+
for( GameSessionListener l : listeners ) {
154+
l.playerLeft(clientId, playerName);
155+
}
156+
}
157+
}
158+
}
159+
160+

0 commit comments

Comments
 (0)