Marauroa Chat Tutorial/Text Client
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
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) {
}
}
}
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
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;
}
}
}
}
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}}