Skip to content

Commit 3546c0b

Browse files
committed
Added a basic (really basic) account service.
1 parent 2844675 commit 3546c0b

File tree

9 files changed

+475
-10
lines changed

9 files changed

+475
-10
lines changed

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

+29-3
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@
5757
import com.simsilica.lemur.OptionPanelState;
5858
import com.simsilica.state.CompositeAppState;
5959

60+
import example.net.AccountSessionListener;
61+
import example.net.client.AccountClientService;
6062
import example.net.client.GameClient;
6163

6264
/**
@@ -110,12 +112,17 @@ public boolean join( String userName ) {
110112
// So here we'd login and then when we get a response from the
111113
// server that we are logged in then we'd launch the game state and
112114
// so on... for now we'll just do it directly.
113-
onLoggedOn();
115+
client.getService(AccountClientService.class).login(userName);
116+
//onLoggedOn();
114117

115118
return true;
116119
}
117120

118-
protected void onLoggedOn() {
121+
protected void onLoggedOn( boolean loggedIn ) {
122+
if( !loggedIn ) {
123+
// We'd want to present an error... but right now this will
124+
// never happen.
125+
}
119126
addChild(new GameSessionState(), true);
120127
}
121128

@@ -205,8 +212,15 @@ public Object call() {
205212
protected void onConnected() {
206213
log.info("onConnected()");
207214
closeConnectingPanel();
215+
216+
// Add our client listeners
217+
client.getService(AccountClientService.class).addAccountSessionListener(new AccountObserver());
218+
219+
String serverInfo = client.getService(AccountClientService.class).getServerInfo();
220+
221+
log.debug("Server info:" + serverInfo);
208222

209-
getStateManager().attach(new LoginState());
223+
getStateManager().attach(new LoginState(serverInfo));
210224
}
211225

212226
protected void onDisconnected( DisconnectInfo info ) {
@@ -268,6 +282,18 @@ public void handleError( Client source, Throwable t ) {
268282
}
269283
}
270284

285+
private class AccountObserver implements AccountSessionListener {
286+
287+
public void notifyLoginStatus( final boolean loggedIn ) {
288+
getApplication().enqueue(new Callable() {
289+
public Object call() {
290+
onLoggedOn(loggedIn);
291+
return null;
292+
}
293+
});
294+
}
295+
}
296+
271297
private class Connector extends Thread {
272298

273299
public Connector() {

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

+21-2
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,11 @@ public class LoginState extends BaseAppState {
5757
private Container loginPanel;
5858
private TextField nameField;
5959

60-
public LoginState() {
60+
private Container serverInfoPanel;
61+
private String serverInfo;
62+
63+
public LoginState( String serverInfo ) {
64+
this.serverInfo = serverInfo;
6165
}
6266

6367
protected void join() {
@@ -102,7 +106,20 @@ protected void initialize( Application app ) {
102106
int width = app.getCamera().getWidth();
103107
int height = app.getCamera().getHeight();
104108

105-
loginPanel.setLocalTranslation(width * 0.5f - prefs.x * 0.5f, height * 0.5f + prefs.y * 0.5f, 10);
109+
loginPanel.setLocalTranslation(width * 0.5f - prefs.x * 0.5f, height * 0.5f + prefs.y * 0.5f, 10);
110+
111+
serverInfoPanel = new Container();
112+
serverInfoPanel.setLocalScale(scale);
113+
serverInfoPanel.addChild(new Label("Server Description", new ElementId("title")));
114+
Label desc = serverInfoPanel.addChild(new Label(serverInfo));
115+
desc.setInsets(new Insets3f(5, 15, 5, 15)); // should leave this up to the style really
116+
desc.setTextHAlignment(HAlignment.Center);
117+
118+
Vector3f prefs2 = serverInfoPanel.getPreferredSize().mult(scale);
119+
serverInfoPanel.setLocalTranslation(width * 0.5f - prefs2.x * 0.5f,
120+
loginPanel.getLocalTranslation().y - prefs.y - 20 * scale,
121+
10);
122+
106123
}
107124

108125
@Override
@@ -113,10 +130,12 @@ protected void cleanup( Application app ) {
113130
protected void onEnable() {
114131
Node root = ((Main)getApplication()).getGuiNode();
115132
root.attachChild(loginPanel);
133+
root.attachChild(serverInfoPanel);
116134
}
117135

118136
@Override
119137
protected void onDisable() {
120138
loginPanel.removeFromParent();
139+
serverInfoPanel.removeFromParent();
121140
}
122141
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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+
* A client's view of the account related services on the server.
43+
*
44+
* @author Paul Speed
45+
*/
46+
public interface AccountSession {
47+
48+
/**
49+
* Returns information about the server. Currently this is just
50+
* a description. It would be better to split this into an asynchronous
51+
* request but this is way simpler. This could be expanded to
52+
* include capabilities, accepted password hashes, and so on.
53+
*/
54+
public String getServerInfo();
55+
56+
/**
57+
* Called by the client to provide the player name for this connection
58+
* and "login" to the game. The server will respond asynchronously with
59+
* a notifyLoginStatus() to the client's AccountSessionListener.
60+
* Note: this could have been done synchronously but synchronous calls
61+
* should generally be avoided when they can. a) it prevents odd logic
62+
* deadlocks if one isn't careful, and b) it makes user interfaces automatically
63+
* more responsive without having to write special background worker code.
64+
* When possible, go asynchronous.
65+
*/
66+
@Asynchronous
67+
public void login( String playerName );
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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 the server-side account service uses
43+
* to send information back to the client. A client will register
44+
* an AccountSessionListener to handle these callbacks.
45+
*
46+
* These are especially important to do asynchronously as they
47+
* might otherwise block other callbacks other services might be
48+
* waiting to make. Also, synchronous callbacks often contribute to
49+
* logical deadlocks.
50+
*
51+
* @author Paul Speed
52+
*/
53+
public interface AccountSessionListener {
54+
55+
/**
56+
* Called by the server to provide login status to the client after
57+
* a login attempt.
58+
*/
59+
@Asynchronous
60+
public void notifyLoginStatus( boolean loggedIn );
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
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.network.service.AbstractClientService;
45+
import com.jme3.network.service.ClientServiceManager;
46+
import com.jme3.network.service.rmi.RmiClientService;
47+
48+
import example.net.AccountSession;
49+
import example.net.AccountSessionListener;
50+
51+
/**
52+
* Provides super-basic account services like logging in. This could
53+
* be expanded to be more complicated based on a real game's needs.
54+
* The basics have been included here as a minimal example that includes
55+
* the basic types of communication necessary.
56+
*
57+
* @author Paul Speed
58+
*/
59+
public class AccountClientService extends AbstractClientService
60+
implements AccountSession {
61+
62+
static Logger log = LoggerFactory.getLogger(AccountClientService.class);
63+
64+
private RmiClientService rmiService;
65+
private AccountSession delegate;
66+
67+
private String playerName;
68+
69+
private AccountSessionCallback sessionCallback = new AccountSessionCallback();
70+
private List<AccountSessionListener> listeners = new CopyOnWriteArrayList<>();
71+
72+
public AccountClientService() {
73+
}
74+
75+
@Override
76+
public String getServerInfo() {
77+
return delegate.getServerInfo();
78+
}
79+
80+
@Override
81+
public void login( String playerName ) {
82+
this.playerName = playerName;
83+
delegate.login(playerName);
84+
}
85+
86+
/**
87+
* Adds a listener that will be notified about account-related events.
88+
* Note that these listeners are called on the networking thread and
89+
* as such are not suitable for modifying the visualization directly.
90+
*/
91+
public void addAccountSessionListener( AccountSessionListener l ) {
92+
listeners.add(l);
93+
}
94+
95+
public void removeAccountSessionListener( AccountSessionListener l ) {
96+
listeners.remove(l);
97+
}
98+
99+
@Override
100+
protected void onInitialize( ClientServiceManager s ) {
101+
log.debug("onInitialize(" + s + ")");
102+
this.rmiService = getService(RmiClientService.class);
103+
if( rmiService == null ) {
104+
throw new RuntimeException("AccountClientService requires RMI service");
105+
}
106+
log.debug("Sharing session callback.");
107+
rmiService.share(sessionCallback, AccountSessionListener.class);
108+
}
109+
110+
/**
111+
* Called during connection setup once the server-side services have been initialized
112+
* for this connection and any shared objects, etc. should be available.
113+
*/
114+
@Override
115+
public void start() {
116+
log.debug("start()");
117+
super.start();
118+
this.delegate = rmiService.getRemoteObject(AccountSession.class);
119+
log.debug("delegate:" + delegate);
120+
if( delegate == null ) {
121+
throw new RuntimeException("No account session found during connection setup");
122+
}
123+
}
124+
125+
/**
126+
* Shared with the server over RMI so that it can notify us about account
127+
* related stuff.
128+
*/
129+
private class AccountSessionCallback implements AccountSessionListener {
130+
131+
@Override
132+
public void notifyLoginStatus( boolean loggedIn ) {
133+
log.trace("notifyLoginStatus(" + loggedIn + ")");
134+
for( AccountSessionListener l : listeners ) {
135+
l.notifyLoginStatus(loggedIn);
136+
}
137+
}
138+
139+
}
140+
}
141+
142+

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@ public GameClient( String host, int port ) throws IOException {
6666

6767
log.info("Adding services...");
6868
client.getServices().addServices(new RpcClientService(),
69-
new RmiClientService()//,
69+
new RmiClientService(),
70+
new AccountClientService()//,
7071
//new EntityClientService(),
71-
//new AccountClientService(),
7272
//new ArenaClient(),
7373
//new ChatClient((short)-2),
7474
//new EtherealClient(ESpaceConstants.OBJECT_PROTOCOL,

0 commit comments

Comments
 (0)