Marauroa Chat Tutorial/Text Client: Difference between revisions
imported>Hendrik Brummermann m header and footer |
imported>Fjacob Cleaned up java code to adhere to the java coding conventions, changed some variable names to be more explicit. |
||
| (20 intermediate revisions by 2 users not shown) | |||
| Line 1: | Line 1: | ||
{{Navigation for Marauroa Top|Using}} |
{{Navigation for Marauroa Top|Using}} |
||
{{Navigation for Marauroa Users}} |
{{Navigation for Marauroa Users}} |
||
{{Marauroa Tutorial}} |
{{Marauroa Chat Tutorial}} |
||
| Line 9: | Line 9: | ||
<source lang="java"> |
<source lang="java"> |
||
import java.util.Map; |
import java.util.Map; |
||
import java.util.List; |
|||
import java.util.HashMap; |
import java.util.HashMap; |
||
import java.util. |
import java.util.List; |
||
import java. |
import java.util.LinkedList; |
||
import marauroa.client.ClientFramework; |
import marauroa.client.ClientFramework; |
||
import marauroa.client.net.IPerceptionListener; |
import marauroa.client.net.IPerceptionListener; |
||
import marauroa.client.net.PerceptionHandler; |
import marauroa.client.net.PerceptionHandler; |
||
import marauroa.common.game.Perception; |
|||
import marauroa.common.game.RPAction; |
import marauroa.common.game.RPAction; |
||
import marauroa.common.game.RPObject; |
import marauroa.common.game.RPObject; |
||
import marauroa.common.game.RPEvent; |
|||
import marauroa.common.net.message.MessageS2CPerception; |
import marauroa.common.net.message.MessageS2CPerception; |
||
import marauroa.common.net.message.TransferContent; |
import marauroa.common.net.message.TransferContent; |
||
| Line 26: | Line 23: | ||
public class Client extends ClientFramework { |
public class Client extends ClientFramework { |
||
private PerceptionHandler handler; |
private PerceptionHandler handler; |
||
private static Client client; |
|||
private Map<RPObject.ID, RPObject> worldObjects; |
|||
private String[] |
private String[] availableCharacters; |
||
private |
private LinkedList<String> quotes = new LinkedList<String>(); |
||
public static Client get() { |
public static Client get() { |
||
| Line 40: | Line 37: | ||
protected Client() { |
protected Client() { |
||
super("log4j.properties"); |
super("log4j.properties"); |
||
worldObjects = new HashMap<RPObject.ID, RPObject>(); |
|||
handler = new PerceptionHandler(new PerceptionListener()); |
handler = new PerceptionHandler(new PerceptionListener()); |
||
} |
} |
||
public String[] |
public String[] getAvailableCharacters() { |
||
return |
return availableCharacters; |
||
} |
} |
||
String popQuote() { |
public String popQuote() { |
||
if (quotes.isEmpty()) { |
if (quotes.isEmpty()) { |
||
return null; |
return null; |
||
} |
} |
||
return quotes.pop(); |
|||
quotes.remove(0); |
|||
return result; |
|||
} |
} |
||
public void |
public void sendMessage(String text) { |
||
RPAction action; |
RPAction action; |
||
action = new RPAction(); |
action = new RPAction(); |
||
| Line 65: | Line 60: | ||
} |
} |
||
@Override |
|||
protected void onPerception(MessageS2CPerception message) { |
protected void onPerception(MessageS2CPerception message) { |
||
try { |
try { |
||
handler.apply(message, |
handler.apply(message, worldObjects); |
||
} catch (java.lang.Exception e) { |
} catch (java.lang.Exception e) { |
||
// Something weird happened while applying perception |
// Something weird happened while applying perception |
||
| Line 73: | Line 69: | ||
} |
} |
||
@Override |
|||
protected List<TransferContent> onTransferREQ(List<TransferContent> items) { |
protected List<TransferContent> onTransferREQ(List<TransferContent> items) { |
||
return items; |
return items; |
||
} |
} |
||
@Override |
|||
protected void onTransfer(List<TransferContent> items) { |
protected void onTransfer(List<TransferContent> items) { |
||
} |
} |
||
@Override |
|||
protected void onAvailableCharacters(String[] characters) { |
protected void onAvailableCharacters(String[] characters) { |
||
availableCharacters = characters; |
|||
} |
} |
||
@Override |
|||
protected void onServerInfo(String[] info) { |
protected void onServerInfo(String[] info) { |
||
for (String s : info) { |
for (String s : info) { |
||
| Line 90: | Line 90: | ||
} |
} |
||
@Override |
|||
protected String getGameName() { |
protected String getGameName() { |
||
return "Chat"; |
return "Chat"; |
||
} |
} |
||
@Override |
|||
protected String getVersionNumber() { |
protected String getVersionNumber() { |
||
return "0.5"; |
return "0.5"; |
||
} |
} |
||
@Override |
|||
protected void onPreviousLogins(List<String> previousLogins) { |
protected void onPreviousLogins(List<String> previousLogins) { |
||
} |
} |
||
class PerceptionListener implements IPerceptionListener { |
class PerceptionListener implements IPerceptionListener { |
||
@Override |
|||
public boolean onAdded(RPObject object) { |
public boolean onAdded(RPObject object) { |
||
if (object.has("text")) { |
if (object.has("text")) { |
||
| Line 109: | Line 113: | ||
} |
} |
||
@Override |
|||
public boolean onModifiedAdded(RPObject object, RPObject changes) { |
public boolean onModifiedAdded(RPObject object, RPObject changes) { |
||
return false; |
return false; |
||
} |
} |
||
@Override |
|||
public boolean onModifiedDeleted(RPObject object, RPObject changes) { |
public boolean onModifiedDeleted(RPObject object, RPObject changes) { |
||
return false; |
return false; |
||
} |
} |
||
@Override |
|||
public boolean onDeleted(RPObject object) { |
public boolean onDeleted(RPObject object) { |
||
return false; |
return false; |
||
} |
} |
||
@Override |
|||
public boolean onMyRPObject(RPObject added, RPObject deleted) { |
public boolean onMyRPObject(RPObject added, RPObject deleted) { |
||
return false; |
return false; |
||
} |
} |
||
@Override |
|||
public void onSynced() { |
public void onSynced() { |
||
} |
} |
||
@Override |
|||
public void onUnsynced() { |
public void onUnsynced() { |
||
} |
} |
||
@Override |
|||
public void onException(Exception e, |
public void onException(Exception e, MessageS2CPerception perception) { |
||
e.printStackTrace(); |
e.printStackTrace(); |
||
System.exit(-1); |
System.exit(-1); |
||
} |
} |
||
@Override |
|||
public boolean onClear() { |
public boolean onClear() { |
||
return false; |
return false; |
||
} |
} |
||
@Override |
|||
public void onPerceptionBegin(byte type, int timestamp) { |
public void onPerceptionBegin(byte type, int timestamp) { |
||
} |
} |
||
@Override |
|||
public void onPerceptionEnd(byte type, int timestamp) { |
public void onPerceptionEnd(byte type, int timestamp) { |
||
} |
} |
||
| Line 161: | Line 175: | ||
Marauroa frameword provides the main method for server, so that you don't need to care about execution loop. It is not true for server, so we will also need to implement the main module of our client. Here is a very easy solution |
Marauroa frameword provides the main method for server, so that you don't need to care about execution loop. It is not true for server, so we will also need to implement the main module of our client. Here is a very easy solution |
||
<source lang="java"> |
<source lang="java"> |
||
import java.io.IOException; |
|||
import java.lang.Thread; |
|||
import marauroa.common.Log4J; |
|||
import marauroa.common.game.RPAction; |
|||
import marauroa.common.game.RPObject; |
import marauroa.common.game.RPObject; |
||
public class Test { |
public class Test { |
||
public static void main(String[] args) { |
public static void main(String[] args) { |
||
boolean |
boolean runClient = true; |
||
Client |
Client client = Client.get(); |
||
try { |
try { |
||
client.connect("localhost", 5555); |
|||
if (args.length == 3) { |
if (args.length == 3) { |
||
client.createAccount(args[0], args[1], args[2]); |
|||
} |
} |
||
client.login(args[0], args[1]); |
|||
if ( |
if (client.getAvailableCharacters().length == 0) { |
||
RPObject character = new RPObject(); |
RPObject character = new RPObject(); |
||
client.createCharacter(args[0], character); |
|||
} |
} |
||
client.chooseCharacter(args[0]); |
|||
} catch (Exception e) { |
} catch (Exception e) { |
||
runClient = false; |
|||
} |
} |
||
int i = 0; |
int i = 0; |
||
while ( |
while (runClient) { |
||
++ |
i++; |
||
client.loop(0); |
|||
if (i % 100 == 50) { |
if (i % 100 == 50) { |
||
client.sendMessage("test" + i); |
|||
} |
} |
||
String s = |
String s = client.popQuote(); |
||
while (s != null) { |
while (s != null) { |
||
System.out.println(s); |
System.out.println(s); |
||
s = |
s = client.popQuote(); |
||
} |
} |
||
try { |
try { |
||
Thread.sleep(100); |
Thread.sleep(100); |
||
} catch (InterruptedException e) { |
} catch (InterruptedException e) { |
||
runClient = false; |
|||
} |
} |
||
} |
} |
||
| Line 224: | Line 233: | ||
java -cp marauroa.jar;log4j.jar;. Test login password |
java -cp marauroa.jar;log4j.jar;. Test login password |
||
</pre> |
</pre> |
||
Again, on Linux & MacOSX, all ";" have to be replaced with ":". |
|||
Don't forget to replace login and password with the actual ones. To create a new account just add a third command-line parameter (that should be an email, but no validation at the moment). |
Don't forget to replace login and password with the actual ones. To create a new account just add a third command-line parameter (that should be an email, but no validation at the moment). |
||
| Line 232: | Line 242: | ||
>java -cp marauroa.jar;log4j.jar;. Test test1 test1 |
>java -cp marauroa.jar;log4j.jar;. Test test1 test1 |
||
Cannot find log4j.properties in classpath. Using default properties. |
Cannot find log4j.properties in classpath. Using default properties. |
||
0.5 |
|||
Do not contact us! |
|||
Marauroa server |
|||
Chat |
|||
*test1* : test50 |
*test1* : test50 |
||
*test1* : test150 |
*test1* : test150 |
||
*test1* : test250 |
|||
... |
|||
</pre> |
</pre> |
||
Note the message about log4j.properties: you can create that file in order to configure the Log4J usage inside Marauroa framework. |
Note the message about log4j.properties: you can create that file in order to configure the Log4J usage inside the Marauroa framework. |
||
== Next Steps == |
== Next Steps == |
||
In the next section of this tutorial, we will write a '''[[Marauroa Tutorial/Swing Client|Swing client]]''' with a graphical user interface. |
In the next section of this tutorial, we will write a '''[[Marauroa Chat Tutorial/Swing Client|Swing client]]''' with a graphical user interface. |
||
[[Category:Marauroa]] |
[[Category:Marauroa]] |
||
{{#breadcrumbs: [[Marauroa]] | [[Navigation for Marauroa Users|Using]] | [[Marauroa Tutorial|Tutorial]] | [[Marauroa Tutorial/Text Client|Text Client]]}} |
{{#breadcrumbs: [[Marauroa]] | [[Navigation for Marauroa Users|Using]] | [[Marauroa Chat Tutorial|Tutorial]] | [[Marauroa Chat Tutorial/Text Client|Text Client]]}} |
||
Latest revision as of 00:26, 15 June 2012
Marauroa Tutorial
Code
In order to create a client using Marauroa frameword you should extend the marauroa.client.ClientFramework class with your logic. Here is the source code for the chat client <source lang="java"> import java.util.Map; import java.util.HashMap; import java.util.List; import java.util.LinkedList;
import marauroa.client.ClientFramework; import marauroa.client.net.IPerceptionListener; import marauroa.client.net.PerceptionHandler; import marauroa.common.game.RPAction; import marauroa.common.game.RPObject; import marauroa.common.net.message.MessageS2CPerception; import marauroa.common.net.message.TransferContent;
public class Client extends ClientFramework {
private PerceptionHandler handler;
private static Client client;
private Map<RPObject.ID, RPObject> worldObjects;
private String[] availableCharacters;
private LinkedList<String> quotes = new LinkedList<String>();
public static Client get() {
if (client == null) {
client = new Client();
}
return client;
}
protected Client() {
super("log4j.properties");
worldObjects = new HashMap<RPObject.ID, RPObject>();
handler = new PerceptionHandler(new PerceptionListener());
}
public String[] getAvailableCharacters() {
return availableCharacters;
}
public String popQuote() {
if (quotes.isEmpty()) {
return null;
}
return quotes.pop();
}
public void sendMessage(String text) {
RPAction action;
action = new RPAction();
action.put("type", "chat");
action.put("text", text);
send(action);
}
@Override
protected void onPerception(MessageS2CPerception message) {
try {
handler.apply(message, worldObjects);
} catch (java.lang.Exception e) {
// Something weird happened while applying perception
}
}
@Override
protected List<TransferContent> onTransferREQ(List<TransferContent> items) {
return items;
}
@Override
protected void onTransfer(List<TransferContent> items) {
}
@Override
protected void onAvailableCharacters(String[] characters) {
availableCharacters = characters;
}
@Override
protected void onServerInfo(String[] info) {
for (String s : info) {
quotes.add(s);
}
}
@Override
protected String getGameName() {
return "Chat";
}
@Override
protected String getVersionNumber() {
return "0.5";
}
@Override
protected void onPreviousLogins(List<String> previousLogins) {
}
class PerceptionListener implements IPerceptionListener {
@Override
public boolean onAdded(RPObject object) {
if (object.has("text")) {
quotes.add("*" + object.get("from") + "* : " + object.get("text"));
}
return false;
}
@Override
public boolean onModifiedAdded(RPObject object, RPObject changes) {
return false;
}
@Override
public boolean onModifiedDeleted(RPObject object, RPObject changes) {
return false;
}
@Override
public boolean onDeleted(RPObject object) {
return false;
}
@Override
public boolean onMyRPObject(RPObject added, RPObject deleted) {
return false;
}
@Override
public void onSynced() {
}
@Override
public void onUnsynced() {
}
@Override
public void onException(Exception e, MessageS2CPerception perception) {
e.printStackTrace();
System.exit(-1);
}
@Override
public boolean onClear() {
return false;
}
@Override
public void onPerceptionBegin(byte type, int timestamp) {
}
@Override
public void onPerceptionEnd(byte type, int timestamp) {
}
}
} </source>
This is mostly a boilerplate code. We claim that we are "Chat" client, version "0.5" As you remember, our server will accept any "Chat" client, without version restrictions.
What you should note is that we use a Marauroa-provided perception handler, see onPerception. Still we introduce our own perception listener to be able to take actions when objects are added, modified, deleted.
PerceptionListener is our implementation for this. We only use onAdded handler, which allows us to react when new object appears, i.e. add the new chat message to the list of messages. The false values returned in other handlers are very important, because they show Marauroa that you would like to continue current action (e.g. adding an object).
All the messages received are stored in the quotes list. One can access stored messages one by one with a popQuote() method.
Finally, you can send a message with a SendMessage method. It constructs an RPAction understandable by server and sends it.
Marauroa frameword provides the main method for server, so that you don't need to care about execution loop. It is not true for server, so we will also need to implement the main module of our client. Here is a very easy solution <source lang="java"> import marauroa.common.game.RPObject;
public class Test {
public static void main(String[] args) {
boolean runClient = true;
Client client = Client.get();
try {
client.connect("localhost", 5555);
if (args.length == 3) {
client.createAccount(args[0], args[1], args[2]);
}
client.login(args[0], args[1]);
if (client.getAvailableCharacters().length == 0) {
RPObject character = new RPObject();
client.createCharacter(args[0], character);
}
client.chooseCharacter(args[0]);
} catch (Exception e) {
runClient = false;
}
int i = 0;
while (runClient) {
i++;
client.loop(0);
if (i % 100 == 50) {
client.sendMessage("test" + i);
}
String s = client.popQuote();
while (s != null) {
System.out.println(s);
s = client.popQuote();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
runClient = false;
}
}
}
} </source>
Our main method does a couple of things. First of all, we create a new account if three command-line arguments are provided (login, password, email). Otherwise we just login with login and password specified.
Then it is time to select a character. If server reports that no characters are available for this account, then we create a new one with the same name as account name. Note, that it is not possible to select a character directly in the onAvailableCharacters handler of the Client class.
A heart-beat loop is started afterwards. At each step we invoke loop(0), where floating point parameter means nothing at this point. Once in 100 steps we send a message to server. There is no interactive way to control messages, still it is amazing to see chat created by your own client.
Deployment
Running a client is very simple, all you need is a bunch of jars (database is not required on the client side) and compiled client code. I use following line for compilation
javac -cp marauroa.jar;log4j.jar;. *.java
Make sure that required jars are in the current directory. To run use
java -cp marauroa.jar;log4j.jar;. Test login password
Again, on Linux & MacOSX, all ";" have to be replaced with ":".
Don't forget to replace login and password with the actual ones. To create a new account just add a third command-line parameter (that should be an email, but no validation at the moment).
Output
Ideally you should see something like
>java -cp marauroa.jar;log4j.jar;. Test test1 test1 Cannot find log4j.properties in classpath. Using default properties. *test1* : test50 *test1* : test150 *test1* : test250 ...
Note the message about log4j.properties: you can create that file in order to configure the Log4J usage inside the Marauroa framework.
Next Steps
In the next section of this tutorial, we will write a Swing client with a graphical user interface. {{#breadcrumbs: Marauroa | Using | Tutorial | Text Client}}