Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorporate server by CMorgue #1

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions rsc-client/.classpath
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry excluding="docs/" including="res/**" kind="src" path=""/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="lib" path="./libs/joml-1.9.19.jar"/>
<classpathentry kind="lib" path="./libs/xpp3.jar"/>
<classpathentry kind="lib" path="./libs/xstream.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry excluding="docs/" including="res/**" kind="src" path=""/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="module" value="true"/>
</attributes>
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this new attribute?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no clue. I didn't edit the .classpath file at all. It had to be automated by some software.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we revert this change?

</classpathentry>
<classpathentry kind="lib" path="libs/joml-1.9.19.jar"/>
<classpathentry kind="lib" path="libs/xpp3.jar"/>
<classpathentry kind="lib" path="libs/xstream.jar"/>
<classpathentry kind="lib" path="libs/netty-3.10.6.Final.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
Binary file added rsc-client/libs/netty-3.10.6.Final.jar
Binary file not shown.
Binary file added rsc-client/res/data/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
250 changes: 108 additions & 142 deletions rsc-client/src/client/Canvas.java

Large diffs are not rendered by default.

21 changes: 14 additions & 7 deletions rsc-client/src/client/Input.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ public class Input implements KeyListener, MouseListener {
/**
* Consumes all input for the current frame.
*
* <p>This should be called every frame after input processing.
* <p>
* This should be called every frame after input processing.
*
* <p>Synchronized because new input could be added at any time!
* <p>
* Synchronized because new input could be added at any time!
*/
public synchronized void consume() {
leftClickReleased = false;
Expand Down Expand Up @@ -71,10 +73,12 @@ public boolean isKeyDown(int key) {
////////////////////////////////////////////////////////////////////////////

@Override
public void mouseClicked(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {
}

@Override
public void mousePressed(MouseEvent e) {}
public void mousePressed(MouseEvent e) {
}

@Override
public void mouseReleased(MouseEvent e) {
Expand All @@ -86,17 +90,20 @@ public void mouseReleased(MouseEvent e) {
}

@Override
public void mouseEntered(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {
}

@Override
public void mouseExited(MouseEvent e) {}
public void mouseExited(MouseEvent e) {
}

////////////////////////////////////////////////////////////////////////////
// KeyListener methods
////////////////////////////////////////////////////////////////////////////

@Override
public void keyTyped(KeyEvent e) {}
public void keyTyped(KeyEvent e) {
}

@Override
public void keyPressed(KeyEvent e) {
Expand Down
208 changes: 192 additions & 16 deletions rsc-client/src/client/RuneClient.java
Original file line number Diff line number Diff line change
@@ -1,37 +1,53 @@
package client;

import java.awt.Dimension;
import java.awt.Image;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

import client.game.Game;
import client.loading.LoadingScreen;
import client.login.LoginScreen;
import client.net.Connection;
import client.net.Packet;

/**
* Class responsible for setting up and running the game.
*
* <p><i>Based on <code>GameShell.java</code> from other RSC sources.</i>
* <p>
* <i>Based on <code>GameShell.java</code> from other RSC sources.</i>
*
* @author Dan Bryce
*/
public class RuneClient {

private static final int WINDOW_WIDTH = 1280;
private static final int WINDOW_HEIGHT = 720;
private final Logger logger = Logger.getLogger(getClass().getName());

private static final int WINDOW_WIDTH = 512;
private static final int WINDOW_HEIGHT = 346;
private static final String WINDOW_TITLE = "OpenRSC";

private static final int MS_PER_FRAME = 16; // 60fps

/**
* Flag used to tell the game to exit.
*
* The original RSC used an exit timer instead, to give the game time to
* finish any outstanding operations before exiting.
* The original RSC used an exit timer instead, to give the game time to finish
* any outstanding operations before exiting.
*/
private boolean exiting;

Expand All @@ -40,14 +56,31 @@ public class RuneClient {
private Canvas canvas;
private BufferedImage screenBuffer;

// Client states
private State state;
private LoadingScreen loadingScreen;
private LoginScreen loginScreen;
private Game game;

// Network
private Connection connection;
private BlockingQueue<Packet> packetQueue = new LinkedBlockingQueue<>();
private long pingLastTime = 0L;

// Packet constants
private final int OPCODE_RSA_HANDSHAKE = 0;
private final int OPCODE_PING = 1;
private final int OPCODE_LOGIN_RESPONSE = 2;

public RuneClient() {
createFrame(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE);

screenBuffer = new BufferedImage(
WINDOW_WIDTH, WINDOW_HEIGHT, BufferedImage.TYPE_INT_RGB);
screenBuffer = new BufferedImage(WINDOW_WIDTH, WINDOW_HEIGHT, BufferedImage.TYPE_INT_RGB);
canvas = new Canvas(screenBuffer);

this.loadingScreen = new LoadingScreen(this);
this.loginScreen = new LoginScreen(this);
this.game = new Game(this);
}

private void createFrame(int width, int height, String title) {
Expand All @@ -58,25 +91,37 @@ private void createFrame(int width, int height, String title) {

// Create the frame itself
frame = new JFrame(title);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setResizable(false);

// Pseudo-fullscreen if window fills the screen
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
frame.setUndecorated(width == screenSize.width &&
height == screenSize.height);
frame.setUndecorated(width == screenSize.width && height == screenSize.height);
frame.setContentPane(gamePanel);
frame.pack();

// Load the frame icon image.
try {
Image icon = ImageIO.read(ClassLoader.getSystemResource("res/data/icon.png"));
if (icon != null) {
frame.setIconImage(icon);
}
} catch (Exception ignore) {
ignore.printStackTrace();
}

// Make the frame visible.
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.toFront();
}

public void load() {

LoadingScreen loadingScreen = new LoadingScreen(this);
// LoadingScreen loadingScreen = new LoadingScreen(this);
state = loadingScreen;

while (!loadingScreen.isLoaded()){
while (!loadingScreen.isLoaded()) {
loadingScreen.continueLoading();
render();
try {
Expand All @@ -89,14 +134,17 @@ public void load() {
}

public void run() {
Thread.currentThread().setName(WINDOW_TITLE);
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);

while (!exiting) {
long before = System.currentTimeMillis();
long before = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());

pollInput();
tick();
render();

int elapsed = (int) (System.currentTimeMillis() - before);
int elapsed = (int) (TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - before);
int sleepTime = MS_PER_FRAME - elapsed;

if (sleepTime < 1) {
Expand Down Expand Up @@ -124,6 +172,7 @@ private void pollInput() {
}

private void tick() {
pollNetwork();
state.tick();
}

Expand All @@ -139,13 +188,17 @@ private void render() {
gamePanel.getGraphics().drawImage(canvas.getImage(), 0, 0, null);
}

public void changeState(State newState) {
private void changeState(State newState) {

// Remove listeners from previous state
Input input = state.getInput();
gamePanel.removeMouseListener(input);
frame.removeKeyListener(input);

// Reset previous state
state.reset();

// Set new state
state = newState;
state.start();

Expand All @@ -155,6 +208,129 @@ public void changeState(State newState) {
frame.addKeyListener(input);
}

/**
* Handles network logic.
*/
private void pollNetwork() {
final long currentTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());

// Send ping.
if (isConnected() && currentTime - pingLastTime > 15000) {
pingLastTime = currentTime;
sendPacket(new Packet(1));
// System.out.println("PING");
}

// Execute the incoming packets.
List<Packet> toProcess = new ArrayList<Packet>();
packetQueue.drainTo(toProcess);
for (Packet packet : toProcess) {

// Opcode 0 reserved for future pre-login handshake.
if (packet.getOpcode() == OPCODE_RSA_HANDSHAKE) {
continue;
}

// Handle pong
if (packet.getOpcode() == OPCODE_PING) {
// System.out.println("PONG");
continue;
}

// Login response
if (packet.getOpcode() == OPCODE_LOGIN_RESPONSE) {
handleLoginResponse(packet);
continue;
}

// The game state is enabled.
// Pass the incoming packet to the game state.
if (state instanceof Game) {
game.executePacket(packet);
continue;
}

}

}

private void handleLoginResponse(Packet packet) {
// The login response code.
boolean loginAccepted = packet.getBoolean();

// Login request accepted.
if (loginAccepted) {
String displayName = packet.getBase37();
int sessionId = packet.getInt();
int privilege = packet.getByte();
game.loggedIn(displayName, sessionId, privilege);
changeState(new Game(this));
return;
}

// Login request denied.
// Read the error message.
String errorMessage = packet.getString();

// TODO pass the errorMessage to the login screen
System.out.println("Login Rejected, Reason: " + errorMessage);
}

/**
* Tries to connect to the server.
*/
public boolean connect(String address, int port) {
if (isConnected()) {
disconnect();
return true;
}
logger.info("Connecting to server " + address + ":" + port);
this.connection = new Connection(this);

if (connection.connect(address, port)) {
logger.info("Connection established");
}
return isConnected();
}

/**
* Disconnects from the server.
*/
public void disconnect() {
connection.disconnect();
connection = null;
packetQueue.clear();

// Go to login screen.
if (state instanceof Game) {
changeState(loginScreen);
}
}

public boolean isConnected() {
return connection != null && connection.isConnected();
}

/**
* Sends a packet to the server.
*/
public void sendPacket(Packet packet) {
connection.sendPacket(packet);
}

/**
* Adds an incoming packet to the queue. Incoming packets have to be queued and
* executed in the main thread to prevent concurrency issues.
*/
public void queuePacket(Packet packet) {
packetQueue.add(packet);
}

// Called by LoadingScreen when loading is complete.
public void onLoaded() {
changeState(loginScreen);
}

public State getState() {
return state;
}
Expand Down
Loading