I've been banging my head against the wall trying to fix this. Pretty sure I lost some hair today.
Basically, we were given a few working classes and were asked to implement RMI on it. There were some errors in the given code to begin with, but I fixed them. Now I get no errors at all, and it isn't working.
Here are the classes:
GameClient.java
- done by me
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class GameClient extends UnicastRemoteObject implements IGUI, IGameEngine, Runnable {
IGameServer ser;
PlayerInfo player;
IGUI g;
IGameEngine ge;
public GameClient(IGameServer ss) throws RemoteException {
ser = ss;
ser.registerClient(this);
}
@Override
public void markedAsOriginalDisplayMode() throws RemoteException {
g.markedAsOriginalDisplayMode();
}
@Override
public void noMarkedDisplayMode() throws RemoteException {
g.noMarkedDisplayMode();
}
@Override
public void normalDisplayMode() throws RemoteException {
g.normalDisplayMode();
}
@Override
public void update() throws RemoteException {
g.update();
}
@Override
public void exit(int i) throws RemoteException {
g = null;
setGUI(g);
ser.setGameOver();
System.exit(i);
}
@Override
public PlayingBlock getNextBlock() throws RemoteException {
return ser.getNextBlock();
}
@Override
public PlayArea getPlayArea() throws RemoteException {
return ser.getPlayArea();
}
@Override
public String getPlayerName() throws RemoteException {
return ser.getPlayerName();
}
@Override
public int getPlayerScore() throws RemoteException {
return ser.getPlayerScore();
}
@Override
public boolean isGameOver() throws RemoteException {
return ser.isGameOver();
}
@Override
public void setGameOver() throws RemoteException {
ser.setGameOver();
}
public void setGUI(IGUI gui) {
g = gui;
}
@Override
public void setPlayer(PlayerInfo pi) {
try {
ser.setPlayer(pi);
}
catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void setPlayerName(String name) throws RemoteException {
ser.setPlayerName(name);
}
@Override
public void start() throws RemoteException {
ser.start();
}
@Override
public void treatEvent(int event) throws RemoteException {
ser.treatEvent(event);
}
@Override
public void run() {
String playerName = "Vasilis";
setPlayer(new PlayerInfo(playerName));
IGUI gui = new GUI(this, 20, 25);
setGUI(gui);
}
}
IGameEngine.java
import java.rmi.RemoteException;
/**
* Interface representing the logic of the game
*/
public interface IGameEngine {
public void treatEvent(int event) throws RemoteException;
public void start() throws RemoteException;
public PlayingBlock getNextBlock() throws RemoteException;
public boolean isGameOver() throws RemoteException;
public void setGameOver() throws RemoteException;
public PlayArea getPlayArea() throws RemoteException;
public int getPlayerScore() throws RemoteException;
public void setPlayerName(String name) throws RemoteException;
public void setPlayer(PlayerInfo pi);
public String getPlayerName() throws RemoteException;
public void setGUI(IGUI gui);
public void exit(int status) throws RemoteException;
}
GameEngine.java
import java.rmi.RemoteException;
import java.util.*;
class GameEngine {
private PlayArea board;
private PlayerInfo player;
private PlayingBlock activeBlock;
private PlayingBlock nextBlock;
private Timer timer;
private TimerTask pendingTask;
private int timerValue;
private float timerScaleFactor;
private int minTimerValue;
private int timerValueChangeInterval;
private int numBlocks;
private Random rng;
private IGUI gui;
private boolean gameOver;
private boolean controlsLocked;
public GameEngine(int size) {
board = new PlayArea(size);
rng = new Random(System.currentTimeMillis());
resetGame();
}
protected void resetGame() {
nextBlock = null;
board.init();
timerScaleFactor = Settings.TIMERSCALE;
minTimerValue = Settings.MINTIMER;
timerValue = Settings.INITTIMER;
timerValueChangeInterval = Settings.CHANGETIMER;
timer = new Timer();
gameOver = false;
controlsLocked = true;
if (player != null) player.reset();
}
public void setGUI(IGUI gui) {
this.gui = gui;
}
private void updateGUI() throws RemoteException {
gui.update();
}
public void setPlayer(PlayerInfo player) {
this.player = player;
}
public String getPlayerName() {
return player.getName();
}
public void setPlayerName(String name) {
player.setName(name);
}
public int getPlayerScore() {
return player.getScore();
}
public PlayArea getPlayArea() {
return board;
}
public boolean isGameOver() {
return gameOver;
}
public void setGameOver() {
this.gameOver = true;
}
public PlayingBlock getNextBlock() {
return nextBlock;
}
// --> game_engine_timer_methods
private void adjustTimerValue() {
if (++numBlocks % timerValueChangeInterval == 0) {
float nextTimerValue = (float) timerValue / timerScaleFactor;
timerValue = nextTimerValue < minTimerValue ? minTimerValue : Math.round(nextTimerValue);
}
}
private void rescheduleTimer() {
stopTimer();
final PlayingBlock block = activeBlock;
pendingTask = new TimerTask() {
public void run() {
synchronized (GameEngine.this) {
if (block.equals(activeBlock)) try {
treatEvent(GameEvent.TIMEOUT);
}
catch (RemoteException e) {
e.printStackTrace();
}
}
}
};
timer.schedule(pendingTask, timerValue);
}
// --<
// --> game_engine_start_method
public void start() throws RemoteException {
resetGame();
nextBlock = new PlayingBlock(rng.nextInt(3) + 1, Colour.randomColour(rng), PlayArea.randomSide(rng));
nextBlock();
updateGUI();
controlsLocked = false;
rescheduleTimer();
}
public void stopTimer() {
if (pendingTask != null) pendingTask.cancel();
}
public void exit(int status) {
System.exit(status);
}
// --<
// --> game_engine_nextBlock_method
private boolean nextBlock() {
int size = board.getSize();
switch (nextBlock.getSide()) {
case PlayArea.NORTHSIDE:
nextBlock.activate(new Coord(size / 2, 1).add(nextBlock.getCoG().neg()));
break;
case PlayArea.SOUTHSIDE:
nextBlock.activate(new Coord(size / 2, size - 2).add(nextBlock.getCoG().neg()));
break;
case PlayArea.EASTSIDE:
nextBlock.activate(new Coord(size - 2, size / 2).add(nextBlock.getCoG().neg()));
break;
case PlayArea.WESTSIDE:
nextBlock.activate(new Coord(1, size / 2).add(nextBlock.getCoG().neg()));
break;
default: // should never occur
break;
}
activeBlock = nextBlock;
if (board.collision(activeBlock)) return false;
else {
board.flipDraw(activeBlock);
nextBlock = new PlayingBlock(rng.nextInt(3) + 1, Colour.randomColour(rng), PlayArea.randomSide(rng));
adjustTimerValue();
return true;
}
}
// --<
// --> game_engine_movement_methods
private boolean rotate() {
return move(null);
}
private boolean slideActiveBlock() {
return move(activeBlock.getSlidingDirection());
}
private boolean move(Direction direction) {
boolean success = false;
// is the move forbidden?
if (direction != null && direction.equals(activeBlock.getSlidingDirection().getReverse())) return false;
board.flipDraw(activeBlock);
if (direction == null) activeBlock.rotate();
else activeBlock.move(direction);
if (!board.collision(activeBlock)) success = true;
else // put activeBlock back
if (direction == null) activeBlock.rotate();
else activeBlock.move(direction.getReverse());
board.flipDraw(activeBlock);
return success;
}
// --<
// --> game_engine_event_handler
synchronized public void treatEvent(int event) throws RemoteException {
if (controlsLocked || gameOver) return;
switch (event) {
case GameEvent.TIMEOUT:
if (slideActiveBlock()) {
rescheduleTimer();
updateGUI();
break;
}
else {
updateScore();
return;
}
case GameEvent.UP:
if (move(Direction.up())) updateGUI();
break;
case GameEvent.DOWN:
if (move(Direction.down())) updateGUI();
break;
case GameEvent.RIGHT:
if (move(Direction.right())) updateGUI();
break;
case GameEvent.LEFT:
if (move(Direction.left())) updateGUI();
break;
case GameEvent.ROTATE:
if (rotate()) updateGUI();
break;
case GameEvent.FLY:
pendingTask.cancel();
pendingTask = null;
while (slideActiveBlock()) ;
updateScore();
return;
default:
// should never occur
}
}
// --<
// --> game_engine_scoring_methods
private void updateScore() {
controlsLocked = true;
new Thread() {
public void run() {
int rounds = 0;
while (findSquares(++rounds)) {
// animate square
for (int i = 0; i < 4; i++) {
if (i % 2 == 0) try {
gui.markedAsOriginalDisplayMode();
}
catch (RemoteException e) {
e.printStackTrace();
}
else try {
gui.normalDisplayMode();
}
catch (RemoteException e) {
e.printStackTrace();
}
try {
updateGUI();
}
catch (RemoteException e) {
e.printStackTrace();
}
try {
Thread.sleep(150);
}
catch (InterruptedException e) {
e.printStackTrace();
exit(1);
}
}
try {
updateBoard();
}
catch (RemoteException e) {
e.printStackTrace();
}
}
if (nextBlock()) {
rescheduleTimer();
}
else {
timer.cancel();
gameOver = true;
}
try {
updateGUI(); // new block is active, display
}
catch (RemoteException e) {
e.printStackTrace();
}
controlsLocked = false; // unlock controls
}
}.start();
}
private boolean findSquares(int round) {
int roundScore = 0;
// brute force
boolean found = false;
int playSize = board.getSize();
for (int size = 3; size >= 2; size--)
for (int x = 0; x < playSize; x++)
for (int y = 0; y < playSize; y++) {
int squareColour = board.getSquareColour(x, y, size);
if (Colour.isPlayBrick(squareColour)) {
roundScore += size * size;
// player.incScore((int) Math.pow(size * size, round));
if (size == 2) {
board.mark(x, y, size);
found = true;
}
}
}
if (roundScore > 0) player.incScore((int) Math.pow(roundScore, round));
return found;
}
public void updateBoard() throws RemoteException {
Direction blockDir = activeBlock.getSlidingDirection();
Direction updateDir = blockDir.getReverse();
Coord checkBrick;
boolean falling;
int size = board.getSize();
gui.noMarkedDisplayMode();
for (int i = 0; i < size; i++) {
falling = false;
if (blockDir.equals(Direction.up())) checkBrick = new Coord(i, 0);
else if (blockDir.equals(Direction.down())) checkBrick = new Coord(i, size - 1);
else if (blockDir.equals(Direction.left())) checkBrick = new Coord(0, i);
else checkBrick = new Coord(size - 1, i);
for (int j = 0; j < size; j++) {
int brickColour = board.getBrick(checkBrick);
if (Colour.isMarked(brickColour)) {
falling = true;
board.setBrick(checkBrick, Colour.BASE);
}
else if (falling) if (Colour.isPlayBrick(brickColour)) {
board.setBrick(checkBrick, Colour.BASE);
PlayingBlock tmpBlock = new PlayingBlock(1, brickColour, PlayArea.NORTHSIDE);
// we just need any temporary block
tmpBlock.activate(checkBrick.add(blockDir.getCoordIncrement()));
while (!board.collision(tmpBlock)) {
// animate the fall
board.flipDraw(tmpBlock);
updateGUI();
try {
Thread.sleep(50);
}
catch (InterruptedException e) {
e.printStackTrace();
exit(1);
}
board.flipDraw(tmpBlock);
tmpBlock.move(blockDir);
}
tmpBlock.move(updateDir);
board.flipDraw(tmpBlock);
}
else {
break; /* stop falling, finish with this loop */
}
checkBrick = checkBrick.add(updateDir.getCoordIncrement());
}
}
gui.normalDisplayMode();
}
// --<
}
IGUI.java
/**
* The server's remote reference to a client
*
*
*/
import java.rmi.*;
public interface IGUI extends Remote {
public void update() throws RemoteException;
public void normalDisplayMode() throws RemoteException;
public void noMarkedDisplayMode() throws RemoteException;
public void markedAsOriginalDisplayMode() throws RemoteException;
}
Squares.java
import java.rmi.RemoteException;
//--> squares_class
class Squares {
public static void main(String[] args) throws RemoteException {
GameClient gc = new GameClient();
String playerName = "Quidam";
if (args.length == 1) if (args[0].length() > 15) playerName = args[0].substring(0, 15);
else playerName = args[0];
gc.setPlayer(new PlayerInfo(playerName));
IGUI gui = new GUI(gc, 20, 25);
gc.setGUI(gui);
}
}
// --<
IGameServer.java
/**
* The interface which defines what operations our server can perform for clients
*
*
*/
import java.rmi.*;
public interface IGameServer extends Remote {
public void registerClient(IGUI clientGUI) throws RemoteException;
public void treatEvent(int event) throws RemoteException;
public void start() throws RemoteException;
public PlayingBlock getNextBlock() throws RemoteException;
public boolean isGameOver() throws RemoteException;
public void setGameOver() throws RemoteException;
public PlayArea getPlayArea() throws RemoteException;
public int getPlayerScore() throws RemoteException;
public void setPlayerName(String name) throws RemoteException;
public void setPlayer(PlayerInfo pi) throws RemoteException;
public String getPlayerName() throws RemoteException;
}
And here is the file I created, GameServer.java
import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.LinkedList;
/**
* Created with IntelliJ IDEA.
* User: akay
* Date: 25/11/2013
* Time: 19:07
* To change this template use File | Settings | File Templates.
*/
public class GameServer extends UnicastRemoteObject implements IGameServer, Serializable {
LinkedList<IGUI> array;
GameEngine ge = new GameEngine(25);
public GameServer() throws RemoteException {
array = new LinkedList();
}
@Override
public PlayingBlock getNextBlock() throws RemoteException {
return ge.getNextBlock();
}
@Override
public PlayArea getPlayArea() throws RemoteException {
return ge.getPlayArea();
}
@Override
public String getPlayerName() {
return ge.getPlayerName();
}
@Override
public int getPlayerScore() {
return ge.getPlayerScore();
}
@Override
public boolean isGameOver() throws RemoteException {
return ge.isGameOver();
}
@Override
public void registerClient(IGUI clientGUI) throws RemoteException {
array.add(clientGUI);
ge.setGUI(clientGUI);
}
@Override
public void setGameOver() throws RemoteException {
ge.setGameOver();
}
@Override
public void setPlayer(PlayerInfo pi) {
ge.setPlayer(pi);
}
@Override
public void setPlayerName(String name) {
ge.setPlayerName(name);
}
@Override
public void start() throws RemoteException {
ge.start();
}
@Override
public void treatEvent(int event) throws RemoteException {
ge.treatEvent(event);
}
public static void main(String args[]) throws RemoteException {
Registry registry;
GameServer s;
final int port = Registry.REGISTRY_PORT;
if (System.getSecurityManager() == null) {
System.setSecurityManager(new java.rmi.RMISecurityManager());
}
try {
registry = LocateRegistry.createRegistry(port);
}
catch (RemoteException e) {
System.out.println("The registry couldn't be created.");
e.printStackTrace();
}
try {
s = new GameServer();
java.rmi.Naming.rebind("Server", s);
}
catch (MalformedURLException e) {
System.out.println("Bad server URI in registration.");
}
catch (RemoteException e) {
System.out.println("Couldn't bind server.");
e.printStackTrace();
}
try {
System.in.read();
}
catch (IOException e) {
}
System.exit(0);
}
}
These files were modified to fix errors and implement RMI. Now, the steps that should be taken are as follows:
GameClient
class which implements our IGUI
and
IGameEngine
interfaces. It will locate the server using the registry,
and will accept local calls from the GUI
, translating them to remote
calls on the server and returning any results.GUI
class so that its reference to a GameEngine
is of type IGameEngine
insteadSquares
class, which will start the client-side part of the game, will now need to create an instance of the GameClient
and pass it to the GUI
as its reference to an IGameEngine
GameServer
class which implements our IGameServer
interface, and also contains a main()
method to start the server and game engineGameEngine
class so that its reference to a GUI
is now of type IGUI
; this will be its remote reference to a client. You will need to catch some exceptions as the GameEngine
is now calling the GUI
remotely.These are the recommended steps. I have implemented all but one, which is the third.
Any help is appreciated.
If you want to download all files in a zip file, here it is: http://akay.me/gameCode.zip
Thank you for any pointers.
EDIT:
I have edited my Squares
class to get the server name from registry by including the following:
String host = null;
Registry registry = LocateRegistry.getRegistry(host);
IGameServer s = (IGameServer) registry.lookup("Server");
GameClient gc = new GameClient(s);
as was suggested.
now I'm getting the following error:
java.rmi.MarshalException: error marshalling arguments; nested exception is:
java.io.NotSerializableException: PlayerInfo
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:138)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:178)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:132)
at com.sun.proxy.$Proxy0.setPlayer(Unknown Source)
at GameClient.setPlayer(GameClient.java:80)
at Squares.main(Squares.java:21)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: java.io.NotSerializableException: PlayerInfo
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1165)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:329)
at sun.rmi.server.UnicastRef.marshalValue(UnicastRef.java:274)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:133)
... 10 more
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at GameEngine.getPlayerName(GameEngine.java:67)
You really need to learn reading error messages. They come with a precise location of the problem, and a description of the problem which is also pretty clear:
constructor GameClient in class GameClient cannot be applied to given types; required: IGameServer found: no arguments reason: actual and formal argument lists differ in length
So, what does that mean? It means that you're calling new GameClient()
, without any argument, whereas the constructor of GameClient is declared as
public GameClient(IGameServer ss) throws RemoteException {
So, you have to pass an object of type IGameServer to the constructor, instead of not passing anything.