diff --git a/rsc-client/.classpath b/rsc-client/.classpath index 86065df..5d94adc 100644 --- a/rsc-client/.classpath +++ b/rsc-client/.classpath @@ -1,10 +1,15 @@ - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/rsc-client/libs/netty-3.10.6.Final.jar b/rsc-client/libs/netty-3.10.6.Final.jar new file mode 100644 index 0000000..b0a1bda Binary files /dev/null and b/rsc-client/libs/netty-3.10.6.Final.jar differ diff --git a/rsc-client/res/data/icon.png b/rsc-client/res/data/icon.png new file mode 100644 index 0000000..9e84661 Binary files /dev/null and b/rsc-client/res/data/icon.png differ diff --git a/rsc-client/src/client/Canvas.java b/rsc-client/src/client/Canvas.java index 0645b7d..b5220f0 100644 --- a/rsc-client/src/client/Canvas.java +++ b/rsc-client/src/client/Canvas.java @@ -9,7 +9,8 @@ /** * Class responsible for storing and manipulating pixel data of a 2D image. * - *

Based on Surface.java from other RSC sources. + *

+ * Based on Surface.java from other RSC sources. * * @author Dan Bryce */ @@ -88,10 +89,9 @@ public void drawSprite(int x, int y, int id) { /* * Bounds checking. * - * If part of the Sprite is offscreen, this ensures that we only draw - * the visible part of the image. Attempting to draw the full image - * would result in parts of the image wrapping onto the next row of - * pixels. + * If part of the Sprite is offscreen, this ensures that we only draw the + * visible part of the image. Attempting to draw the full image would result in + * parts of the image wrapping onto the next row of pixels. */ if (y < 0) { @@ -125,36 +125,37 @@ public void drawSprite(int x, int y, int id) { return; } - setPixels(pixels, sprite.getPixels(), - sourceIndex, targetIndex, - spriteWidth, spriteHeight, - screenRowIncrement, spriteRowIncrement); + setPixels(pixels, sprite.getPixels(), sourceIndex, targetIndex, spriteWidth, spriteHeight, screenRowIncrement, + spriteRowIncrement); } /** * Copies a block of pixels from the source to the target. * - * @param target Target pixel data. - * @param source Source pixel data. - * @param sourceIndex Starting index for the source array. - * @param targetIndex Starting index for the target array. - * @param sourceWidth Width of the source image. - * @param sourceHeight Height of the source image. + * @param target + * Target pixel data. + * @param source + * Source pixel data. + * @param sourceIndex + * Starting index for the source array. + * @param targetIndex + * Starting index for the target array. + * @param sourceWidth + * Width of the source image. + * @param sourceHeight + * Height of the source image. * @param targetRowIncrement - * Value to add to the target index after each row is copied. + * Value to add to the target index after each row is copied. * @param sourceRowIncrement - * Value to add to the source index after each row is copied. + * Value to add to the source index after each row is copied. */ - private static void setPixels( - int target[], int source[], - int sourceIndex, int targetIndex, - int sourceWidth, int sourceHeight, - int targetRowIncrement, int sourceRowIncrement) { + private static void setPixels(int target[], int source[], int sourceIndex, int targetIndex, int sourceWidth, + int sourceHeight, int targetRowIncrement, int sourceRowIncrement) { /* - * The original source code copied multiple pixels at a time inside the - * loop body, presumably intended as some kind of optimisation. Here I - * have favoured simplicity over efficiency. + * The original source code copied multiple pixels at a time inside the loop + * body, presumably intended as some kind of optimisation. Here I have favoured + * simplicity over efficiency. */ for (int y = 0; y < sourceHeight; y++) { for (int x = 0; x < sourceWidth; x++) { @@ -268,19 +269,8 @@ private void plotSale(int texturePixels[], int i, int j, int k, int l, int i1, i * @param paramD * @param paramDModifier */ - public void renderScanline_LargeTexture( - int texturePixels[], - int i, - int j, - int paramA, - int paramB, - int paramC, - int paramAModifier, - int paramBModifier, - int paramCModifier, - int length, - int pxOffset, - int paramD, + public void renderScanline_LargeTexture(int texturePixels[], int i, int j, int paramA, int paramB, int paramC, + int paramAModifier, int paramBModifier, int paramCModifier, int length, int pxOffset, int paramD, int paramDModifier) { if (length <= 0) { @@ -424,20 +414,8 @@ public void renderScanline_LargeTexture( } } - public void renderScanline_LargeTranslucentTexture( - int texturePixels[], - int texOffset, - int texStart, - int k, - int l, - int i1, - int j1, - int k1, - int l1, - int length, - int pxIndex, - int k2, - int l2) { + public void renderScanline_LargeTranslucentTexture(int texturePixels[], int texOffset, int texStart, int k, int l, + int i1, int j1, int k1, int l1, int length, int pxIndex, int k2, int l2) { if (length <= 0) { return; @@ -481,64 +459,80 @@ public void renderScanline_LargeTranslucentTexture( texOffset += k2 & 0x600000; colorShift = k2 >> 23; k2 += l2; - pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; texOffset = (texOffset & 0x3fff) + (k2 & 0x600000); colorShift = k2 >> 23; k2 += l2; - pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; texOffset = (texOffset & 0x3fff) + (k2 & 0x600000); colorShift = k2 >> 23; k2 += l2; - pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; texOffset = (texOffset & 0x3fff) + (k2 & 0x600000); colorShift = k2 >> 23; k2 += l2; - pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset = i3; texStart = j3; @@ -568,27 +562,15 @@ public void renderScanline_LargeTranslucentTexture( colorShift = k2 >> 23; k2 += l2; } - pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0x3f80) + (texOffset >> 7)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; } } - public void renderScanline_LargeTextureWithTransparency( - int i, - int texOffset, - int texStart, - int texturePixels[], - int l, - int i1, - int j1, - int k1, - int l1, - int i2, - int length, - int pxIndex, - int l2, - int i3) { + public void renderScanline_LargeTextureWithTransparency(int i, int texOffset, int texStart, int texturePixels[], + int l, int i1, int j1, int k1, int l1, int i2, int length, int pxIndex, int l2, int i3) { if (length <= 0) { return; @@ -788,20 +770,8 @@ public void renderScanline_LargeTextureWithTransparency( /* * Used for wooden floors! */ - public void renderScanline_SmallTexture( - int texturePixels[], - int texOffset, - int texStart, - int k, - int l, - int i1, - int j1, - int k1, - int l1, - int length, - int pxIndex, - int k2, - int l2) { + public void renderScanline_SmallTexture(int texturePixels[], int texOffset, int texStart, int k, int l, int i1, + int j1, int k1, int l1, int length, int pxIndex, int k2, int l2) { if (length <= 0) { return; @@ -912,20 +882,8 @@ public void renderScanline_SmallTexture( } - public void renderScanline_SmallTranslucentTexture( - int texturePixels[], - int texOffset, - int texStart, - int k, - int l, - int i1, - int j1, - int k1, - int l1, - int length, - int pxIndex, - int k2, - int l2) { + public void renderScanline_SmallTranslucentTexture(int texturePixels[], int texOffset, int texStart, int k, int l, + int i1, int j1, int k1, int l1, int length, int pxIndex, int k2, int l2) { if (length <= 0) { return; @@ -965,7 +923,8 @@ public void renderScanline_SmallTranslucentTexture( k2 += l2; if (i4 < 16) { for (int k4 = 0; k4 < i4; k4++) { - pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; if ((k4 & 3) == 3) { @@ -976,78 +935,84 @@ public void renderScanline_SmallTranslucentTexture( } } else { - pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; texOffset = (texOffset & 0xfff) + (k2 & 0xc0000); colorShift = k2 >> 20; k2 += l2; - pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; texOffset = (texOffset & 0xfff) + (k2 & 0xc0000); colorShift = k2 >> 20; k2 += l2; - pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; texOffset = (texOffset & 0xfff) + (k2 & 0xc0000); colorShift = k2 >> 20; k2 += l2; - pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); texOffset += texOffsetStride; texStart += texStartStride; - pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + (pixels[pxIndex] >> 1 & 0x7f7f7f); + pixels[pxIndex++] = (texturePixels[(texStart & 0xfc0) + (texOffset >> 6)] >>> colorShift) + + (pixels[pxIndex] >> 1 & 0x7f7f7f); } } } - public void renderScanline_SmallTextureWithTransparency( - int texturePixels[], - int l, - int i1, - int j1, - int k1, - int l1, - int i2, - int length, - int pxIndex, - int l2, - int i3) { + public void renderScanline_SmallTextureWithTransparency(int texturePixels[], int l, int i1, int j1, int k1, int l1, + int i2, int length, int pxIndex, int l2, int i3) { if (length <= 0) { return; @@ -1215,7 +1180,8 @@ public void renderScanline_SmallTextureWithTransparency( /* * No idea what this is used for. */ - public void renderScanline_TranslucentGradient(int length, int pxIndex, int gradient[], int gradientIndex, int stride) { + public void renderScanline_TranslucentGradient(int length, int pxIndex, int gradient[], int gradientIndex, + int stride) { if (length < 0) { return; diff --git a/rsc-client/src/client/Input.java b/rsc-client/src/client/Input.java index 26c79ab..0000d90 100644 --- a/rsc-client/src/client/Input.java +++ b/rsc-client/src/client/Input.java @@ -27,9 +27,11 @@ public class Input implements KeyListener, MouseListener { /** * Consumes all input for the current frame. * - *

This should be called every frame after input processing. + *

+ * This should be called every frame after input processing. * - *

Synchronized because new input could be added at any time! + *

+ * Synchronized because new input could be added at any time! */ public synchronized void consume() { leftClickReleased = false; @@ -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) { @@ -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) { diff --git a/rsc-client/src/client/RuneClient.java b/rsc-client/src/client/RuneClient.java index a994c99..3bb2e23 100644 --- a/rsc-client/src/client/RuneClient.java +++ b/rsc-client/src/client/RuneClient.java @@ -1,28 +1,44 @@ 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. * - *

Based on GameShell.java from other RSC sources. + *

+ * Based on GameShell.java from other RSC sources. * * @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 @@ -30,8 +46,8 @@ public class RuneClient { /** * 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; @@ -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 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) { @@ -58,14 +91,26 @@ 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(); @@ -73,10 +118,10 @@ private void createFrame(int width, int height, String title) { 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 { @@ -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) { @@ -124,6 +172,7 @@ private void pollInput() { } private void tick() { + pollNetwork(); state.tick(); } @@ -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(); @@ -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 toProcess = new ArrayList(); + 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; } diff --git a/rsc-client/src/client/State.java b/rsc-client/src/client/State.java index cf9739e..ff1b7ad 100644 --- a/rsc-client/src/client/State.java +++ b/rsc-client/src/client/State.java @@ -10,14 +10,19 @@ public State(RuneClient launcher) { this.launcher = launcher; } - public void start() {} + public void start() { + } - public void pollInput() {} + public void pollInput() { + } - public void tick() {} + public void tick() { + } public abstract StateRenderer getRenderer(); + public abstract void reset(); + public Input getInput() { return input; } diff --git a/rsc-client/src/client/entityhandling/defs/AnimationDef.java b/rsc-client/src/client/entityhandling/defs/AnimationDef.java index c7c1501..77eaf39 100644 --- a/rsc-client/src/client/entityhandling/defs/AnimationDef.java +++ b/rsc-client/src/client/entityhandling/defs/AnimationDef.java @@ -4,7 +4,7 @@ * Data relating to an animation. */ public class AnimationDef { - + public String name; public int charColour; public int genderModel; @@ -35,5 +35,5 @@ public boolean hasF() { public int getNumber() { return number; } - + } \ No newline at end of file diff --git a/rsc-client/src/client/entityhandling/defs/DoorDef.java b/rsc-client/src/client/entityhandling/defs/DoorDef.java index e30e7ba..bae260a 100644 --- a/rsc-client/src/client/entityhandling/defs/DoorDef.java +++ b/rsc-client/src/client/entityhandling/defs/DoorDef.java @@ -6,11 +6,11 @@ public class DoorDef extends EntityDef { public String command1; - + public String command2; - + public int doorType; - + public int unknown; public int modelVar1; @@ -44,5 +44,5 @@ public int getFrontTexture() { public int getBackTexture() { return modelVar3; } - + } diff --git a/rsc-client/src/client/entityhandling/defs/ElevationDef.java b/rsc-client/src/client/entityhandling/defs/ElevationDef.java index a687b36..23b4039 100644 --- a/rsc-client/src/client/entityhandling/defs/ElevationDef.java +++ b/rsc-client/src/client/entityhandling/defs/ElevationDef.java @@ -4,7 +4,7 @@ * Data relating to a point of elevation. */ public class ElevationDef { - + public int unknown1; public int unknown2; @@ -15,5 +15,5 @@ public int getUnknown1() { public int getUnknown2() { return unknown2; } - + } \ No newline at end of file diff --git a/rsc-client/src/client/entityhandling/defs/EntityDef.java b/rsc-client/src/client/entityhandling/defs/EntityDef.java index c0120b9..4f00098 100644 --- a/rsc-client/src/client/entityhandling/defs/EntityDef.java +++ b/rsc-client/src/client/entityhandling/defs/EntityDef.java @@ -3,13 +3,13 @@ /** * Base class for objects containing data loaded from XML. * - * The field names in this class (and child classes) reflect the named - * parameters present in the game's XML files, so they cannot be changed - * unless the corresponding XML files are changed as well. + * The field names in this class (and child classes) reflect the named + * parameters present in the game's XML files, so they cannot be changed unless + * the corresponding XML files are changed as well. * * Similarly, any changes to the package or class name must be reflected in the - * initialisation of the Resources class, where aliases are set up for the - * XML parser. + * initialisation of the Resources class, where aliases are set up for the XML + * parser. */ public abstract class EntityDef { @@ -17,7 +17,7 @@ public abstract class EntityDef { * The name of the entity. */ public String name; - + /** * The description of the entity. */ @@ -36,5 +36,5 @@ public String getName() { public String getDescription() { return description; } - + } diff --git a/rsc-client/src/client/entityhandling/defs/GameObjectDef.java b/rsc-client/src/client/entityhandling/defs/GameObjectDef.java index e217560..c8b47cd 100644 --- a/rsc-client/src/client/entityhandling/defs/GameObjectDef.java +++ b/rsc-client/src/client/entityhandling/defs/GameObjectDef.java @@ -8,13 +8,13 @@ public class GameObjectDef extends EntityDef { public String command1; public String command2; - + public int type; - + public int width; - + public int height; - + /** * Height of items placed atop this object. * @@ -54,5 +54,5 @@ public int getHeight() { public int getGroundItemHeight() { return groundItemZ; } - + } diff --git a/rsc-client/src/client/entityhandling/defs/ItemDef.java b/rsc-client/src/client/entityhandling/defs/ItemDef.java index 1b136da..412f42f 100644 --- a/rsc-client/src/client/entityhandling/defs/ItemDef.java +++ b/rsc-client/src/client/entityhandling/defs/ItemDef.java @@ -6,15 +6,15 @@ public class ItemDef extends EntityDef { public String command; - + public int basePrice; - + public int sprite; - + public boolean stackable; - + public boolean wieldable; - + public int pictureMask; public String getCommand() { diff --git a/rsc-client/src/client/entityhandling/defs/ItemDropDef.java b/rsc-client/src/client/entityhandling/defs/ItemDropDef.java index 720e436..8c33f94 100644 --- a/rsc-client/src/client/entityhandling/defs/ItemDropDef.java +++ b/rsc-client/src/client/entityhandling/defs/ItemDropDef.java @@ -4,7 +4,7 @@ * Data relating to an inventory item that has been dropped. */ public class ItemDropDef { - + public int id; public int amount; public int weight; @@ -20,5 +20,5 @@ public int getAmount() { public int getWeight() { return weight; } - + } \ No newline at end of file diff --git a/rsc-client/src/client/entityhandling/defs/NpcDef.java b/rsc-client/src/client/entityhandling/defs/NpcDef.java index c56f0cb..50355fc 100644 --- a/rsc-client/src/client/entityhandling/defs/NpcDef.java +++ b/rsc-client/src/client/entityhandling/defs/NpcDef.java @@ -4,28 +4,28 @@ * Data relating to an NPC. */ public class NpcDef extends EntityDef { - + public String command; - + public int[] sprites; public int hairColour; public int topColour; public int bottomColour; public int skinColour; - + public int camera1; public int camera2; - + public int walkModel; public int combatModel; public int combatSprite; - + public int hits; public int attack; public int defense; public int strength; - + public boolean attackable; public int respawnTime; public boolean aggressive; @@ -106,5 +106,5 @@ public int respawnTime() { public boolean isAggressive() { return aggressive; } - + } diff --git a/rsc-client/src/client/entityhandling/defs/PrayerDef.java b/rsc-client/src/client/entityhandling/defs/PrayerDef.java index 8f09445..4255c39 100644 --- a/rsc-client/src/client/entityhandling/defs/PrayerDef.java +++ b/rsc-client/src/client/entityhandling/defs/PrayerDef.java @@ -9,7 +9,7 @@ public class PrayerDef extends EntityDef { * The minimum Prayer level required to use the prayer. */ public int reqLevel; - + /** * The drain rate of the prayer (units unknown - possibly points per min). */ diff --git a/rsc-client/src/client/entityhandling/defs/SpellDef.java b/rsc-client/src/client/entityhandling/defs/SpellDef.java index 3168589..072ef34 100644 --- a/rsc-client/src/client/entityhandling/defs/SpellDef.java +++ b/rsc-client/src/client/entityhandling/defs/SpellDef.java @@ -13,19 +13,19 @@ public class SpellDef extends EntityDef { * The minimum Magic level required to use the spell. */ public int reqLevel; - + public int type; - + /** * The number of different runes needed for the spell. */ public int runeCount; - + /** * The number of each type of rune required, keyed by item ID. */ public HashMap requiredRunes; - + /** * The amount of experience awarded by casting this spell. */ diff --git a/rsc-client/src/client/entityhandling/defs/TextureDef.java b/rsc-client/src/client/entityhandling/defs/TextureDef.java index d230cc3..b34c4f8 100644 --- a/rsc-client/src/client/entityhandling/defs/TextureDef.java +++ b/rsc-client/src/client/entityhandling/defs/TextureDef.java @@ -4,7 +4,7 @@ * Data relating to a texture. */ public class TextureDef { - + public String dataName; public String animationName; @@ -15,5 +15,5 @@ public String getDataName() { public String getAnimationName() { return animationName; } - + } \ No newline at end of file diff --git a/rsc-client/src/client/entityhandling/defs/TileDef.java b/rsc-client/src/client/entityhandling/defs/TileDef.java index 0e6a9c0..a38eccb 100644 --- a/rsc-client/src/client/entityhandling/defs/TileDef.java +++ b/rsc-client/src/client/entityhandling/defs/TileDef.java @@ -4,9 +4,9 @@ * Data relating to a tile of the game world. */ public class TileDef { - + public static final int TYPE_BRIDGE = 4; - + public int colour; public int unknown; public int objectType; @@ -22,5 +22,5 @@ public int getType() { public int getObjectType() { return objectType; } - + } \ No newline at end of file diff --git a/rsc-client/src/client/game/Game.java b/rsc-client/src/client/game/Game.java index ccbb4bc..24cb918 100644 --- a/rsc-client/src/client/game/Game.java +++ b/rsc-client/src/client/game/Game.java @@ -1,8 +1,8 @@ package client.game; import java.awt.event.KeyEvent; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.logging.Level; +import java.util.logging.Logger; import client.RuneClient; import client.State; @@ -16,51 +16,46 @@ import client.game.scene.Scene; import client.game.world.World; import client.game.world.WorldLoader; -import client.loading.LoadingScreen; -import client.login.LoginScreen; -import client.net.Connection; -import client.packets.Packet; -import client.packets.PacketHandler; -import client.packets.PacketHandlers; +import client.net.Packet; /** * State responsible for running the game. * - *

Based on mudclient.java from other RSC sources. + *

+ * Based on mudclient.java from other RSC sources. * * @author Dan Bryce */ public class Game extends State { + private final Logger logger = Logger.getLogger(getClass().getName()); + public static final int SPAWN_SECTOR_X = 50; public static final int SPAWN_SECTOR_Z = 51; - private ExecutorService executor; - private Connection connection; - private WorldLoader worldLoader; private World world; private Scene scene; private Mob player; private GameRenderer renderer; - private LoadingScreen loadingScreen; - private LoginScreen loginScreen; // 192 = West // 128 = North - // 64 = East - // 0 = South + // 64 = East + // 0 = South private int cameraRotation = 128; private int cameraPositionX; private int cameraPositionZ; private int cameraHeight = Camera.DEFAULT_HEIGHT; - public Game(RuneClient launcher, Connection connection) { - super(launcher); - - this.connection = connection; + // Server session + private String displayName; + private int sessionId; + private int privilege; + public Game(RuneClient launcher) { + super(launcher); scene = new Scene(); world = new World(scene); worldLoader = new WorldLoader(world); @@ -69,8 +64,6 @@ public Game(RuneClient launcher, Connection connection) { @Override public void start() { - executor = Executors.newCachedThreadPool(); - executor.execute(connection.getPacketReaderThread()); } @Override @@ -143,8 +136,15 @@ private void groundTileSelected(int tileX, int tileZ) { @Override public void tick() { + // Check for unexpected disconnection. + if (player != null && !launcher.isConnected()) { + logger.log(Level.WARNING, "Connection Lost!"); + launcher.disconnect(); + return; + } - handlePackets(); + // Client is connected. + // Put any network game logic below. if (player == null) { // Not yet logged in @@ -152,14 +152,24 @@ public void tick() { } updateCamera(); + } - private void handlePackets() { - for (Packet p : connection.getPacketsReceived()) { - PacketHandler handler = PacketHandlers.get(p.id); - if (handler != null) { - handler.apply(p, this); - } + /** + * Executes an incoming packet. + */ + public void executePacket(Packet packet) { + switch (packet.getOpcode()) { + case 3: + // Read chatbox message + int icon = packet.getByte(); + String message = packet.getString(); + System.out.println("[icon-" + icon + "]" + message); + break; + default: + logger.log(Level.WARNING, + "Unhandled Packet, opcode: " + packet.getOpcode() + ", length: " + packet.getPacketLength()); + break; } } @@ -201,7 +211,15 @@ private void loadSectors() { } } - public void loggedIn() { + /** + * Executed when the server accepts our login request. + */ + public void loggedIn(String displayName, int sessionId, int privilege) { + // Get the server variables for later. + this.displayName = displayName; + this.sessionId = sessionId; + this.privilege = privilege; + // Player position is relative to the World origin player = new Mob(); player.x = 66 * World.TILE_WIDTH; @@ -209,12 +227,12 @@ public void loggedIn() { worldLoader.loadSector(SPAWN_SECTOR_X, SPAWN_SECTOR_Z); } - public LoadingScreen getLoadingScreen() { - return loadingScreen; - } - - public LoginScreen getLoginScreen() { - return loginScreen; + @Override + public void reset() { + displayName = ""; + sessionId = -1; + privilege = -1; + player = null; } public Scene getScene() { diff --git a/rsc-client/src/client/game/render/GameRenderer.java b/rsc-client/src/client/game/render/GameRenderer.java index b6ffdbb..4fb8887 100644 --- a/rsc-client/src/client/game/render/GameRenderer.java +++ b/rsc-client/src/client/game/render/GameRenderer.java @@ -9,12 +9,13 @@ /** * Class responsible for rendering the game. * - *

The general idea is: + *

+ * The general idea is: * *

* * @author Dan Bryce @@ -43,9 +44,7 @@ public GameRenderer(Game game) { scene = game.getScene(); sceneBuilder = new SceneBuilder(scene, world); - softwareRenderer = new SoftwareRenderer(scene, - game.getLauncher().getWidth(), - game.getLauncher().getHeight()); + softwareRenderer = new SoftwareRenderer(scene, game.getLauncher().getWidth(), game.getLauncher().getHeight()); mousePicker = softwareRenderer.getMousePicker(); } @@ -77,17 +76,11 @@ private void renderUi(Canvas canvas) { } private void renderMenus(Canvas canvas) { - canvas.drawSprite( - game.getLauncher().getWidth() - MENUS_OFFSET_X, - MENUS_OFFSET_Y, - SPRITE_ID_MENUS); + canvas.drawSprite(game.getLauncher().getWidth() - MENUS_OFFSET_X, MENUS_OFFSET_Y, SPRITE_ID_MENUS); } private void renderInventoryMenu(Canvas canvas) { - canvas.drawSprite( - game.getLauncher().getWidth() - INVENTORY_MENU_OFFSET_X, - MENUS_OFFSET_Y, - SPRITE_ID_INVENTORY); + canvas.drawSprite(game.getLauncher().getWidth() - INVENTORY_MENU_OFFSET_X, MENUS_OFFSET_Y, SPRITE_ID_INVENTORY); } } diff --git a/rsc-client/src/client/game/render/MousePicker.java b/rsc-client/src/client/game/render/MousePicker.java index ee05d9e..0dad076 100644 --- a/rsc-client/src/client/game/render/MousePicker.java +++ b/rsc-client/src/client/game/render/MousePicker.java @@ -8,9 +8,9 @@ public class MousePicker { private int mouseX; private int mouseY; - + private int baseX; - + private int mousePickedCount; private Model mousePickedModels[] = new Model[MAX_MOUSE_PICKS]; private int mousePickedFaces[] = new int[MAX_MOUSE_PICKS]; @@ -28,19 +28,19 @@ public void add(Model gameModel, int faceId) { public int getMousePickedCount() { return mousePickedCount; } - + public Model[] getMousePickedModels() { return mousePickedModels; } - + public int[] getMousePickedFaces() { return mousePickedFaces; } - + public int getMouseX() { return mouseX; } - + public int getMouseY() { return mouseY; } diff --git a/rsc-client/src/client/game/render/SoftwareRenderer.java b/rsc-client/src/client/game/render/SoftwareRenderer.java index 7aa34a3..a0f8c42 100644 --- a/rsc-client/src/client/game/render/SoftwareRenderer.java +++ b/rsc-client/src/client/game/render/SoftwareRenderer.java @@ -13,12 +13,15 @@ /** * Class responsible for rendering a scene. * - *

This is essentially a 3d software renderer; consider porting to OpenGL. + *

+ * This is essentially a 3d software renderer; consider porting to OpenGL. * - *

This class should not be aware of the game at all, and the game should - * not be aware of what goes on inside this class. + *

+ * This class should not be aware of the game at all, and the game should not be + * aware of what goes on inside this class. * - *

Based on client.Scene from EasyRSC. + *

+ * Based on client.Scene from EasyRSC. */ public class SoftwareRenderer { @@ -87,11 +90,7 @@ public SoftwareRenderer(Scene scene, int width, int height) { visiblePolygons[l] = new Polygon(); } - setBounds( - width / 2, height / 2, - width / 2, height / 2, - width, - VIEW_DISTANCE); + setBounds(width / 2, height / 2, width / 2, height / 2, width, VIEW_DISTANCE); } public void render(Canvas canvas) { @@ -106,8 +105,7 @@ public void render(Canvas canvas) { scene.getModels()[i].project(camera, viewDistance, clipNear); } - scene.getModels()[scene.getNumModels()] - .project(camera, viewDistance, clipNear); + scene.getModels()[scene.getNumModels()].project(camera, viewDistance, clipNear); visiblePolygonCount = 0; // Draw each model in the scene @@ -287,7 +285,6 @@ public void render(Canvas canvas) { } } - if (polygonModel.verticesProjected[vertexIndexInModel].z >= clipNear) { planeX[plane] = polygonModel.verticesView[vertexIndexInModel].x; @@ -295,7 +292,8 @@ public void render(Canvas canvas) { vertexShade[plane] = light; if (polygonModel.verticesProjected[vertexIndexInModel].z > scene.fogZDistance) { - vertexShade[plane] += (polygonModel.verticesProjected[vertexIndexInModel].z - scene.fogZDistance) / scene.fogZFalloff; + vertexShade[plane] += (polygonModel.verticesProjected[vertexIndexInModel].z + - scene.fogZDistance) / scene.fogZFalloff; } plane++; @@ -310,12 +308,15 @@ public void render(Canvas canvas) { } if (polygonModel.verticesProjected[vertEnd].z >= clipNear) { - int k7 = polygonModel.verticesProjected[vertexIndexInModel].z - polygonModel.verticesProjected[vertEnd].z; + int k7 = polygonModel.verticesProjected[vertexIndexInModel].z + - polygonModel.verticesProjected[vertEnd].z; int i5 = polygonModel.verticesProjected[vertexIndexInModel].x - - ((polygonModel.verticesProjected[vertexIndexInModel].x - polygonModel.verticesProjected[vertEnd].x) + - ((polygonModel.verticesProjected[vertexIndexInModel].x + - polygonModel.verticesProjected[vertEnd].x) * (polygonModel.verticesProjected[vertexIndexInModel].z - clipNear)) / k7; int j6 = polygonModel.verticesProjected[vertexIndexInModel].y - - ((polygonModel.verticesProjected[vertexIndexInModel].y - polygonModel.verticesProjected[vertEnd].y) + - ((polygonModel.verticesProjected[vertexIndexInModel].y + - polygonModel.verticesProjected[vertEnd].y) * (polygonModel.verticesProjected[vertexIndexInModel].z - clipNear)) / k7; planeX[plane] = (i5 << viewDistance) / clipNear; planeY[plane] = (j6 << viewDistance) / clipNear; @@ -330,12 +331,15 @@ public void render(Canvas canvas) { } if (polygonModel.verticesProjected[vertEnd].z >= clipNear) { - int l7 = polygonModel.verticesProjected[vertexIndexInModel].z - polygonModel.verticesProjected[vertEnd].z; + int l7 = polygonModel.verticesProjected[vertexIndexInModel].z + - polygonModel.verticesProjected[vertEnd].z; int j5 = polygonModel.verticesProjected[vertexIndexInModel].x - - ((polygonModel.verticesProjected[vertexIndexInModel].x - polygonModel.verticesProjected[vertEnd].x) + - ((polygonModel.verticesProjected[vertexIndexInModel].x + - polygonModel.verticesProjected[vertEnd].x) * (polygonModel.verticesProjected[vertexIndexInModel].z - clipNear)) / l7; int k6 = polygonModel.verticesProjected[vertexIndexInModel].y - - ((polygonModel.verticesProjected[vertexIndexInModel].y - polygonModel.verticesProjected[vertEnd].y) + - ((polygonModel.verticesProjected[vertexIndexInModel].y + - polygonModel.verticesProjected[vertEnd].y) * (polygonModel.verticesProjected[vertexIndexInModel].z - clipNear)) / l7; planeX[plane] = (j5 << viewDistance) / clipNear; planeY[plane] = (k6 << viewDistance) / clipNear; @@ -370,8 +374,7 @@ public void render(Canvas canvas) { } } - private void renderSprite(Model polygonModel, int polyFace, - Canvas canvas) { + private void renderSprite(Model polygonModel, int polyFace, Canvas canvas) { SpriteEntity spriteEntity = scene.getSpriteEntities()[polyFace]; int faceverts[] = polygonModel.faceVertices[polyFace]; int face0 = faceverts[0]; @@ -515,17 +518,8 @@ private boolean polygonsOrder(Polygon[] polygons, int start, int end) { } while (true); } - private void generateScanlines( - int startX, - int endX, - int y, - int startS, - int plane, - int planeX[], - int planeY[], - int vertexShade[], - Model gameModel, - int faceId) { + private void generateScanlines(int startX, int endX, int y, int startS, int plane, int planeX[], int planeY[], + int vertexShade[], Model gameModel, int faceId) { if (plane == 3) { @@ -1124,16 +1118,15 @@ private void generateScanlines( if (mouseY >= minY && mouseY < maxY) { Scanline scanline = scanlines[mouseY]; - if (mouseX >= scanline.startX >> 8 && - mouseX <= scanline.endX >> 8 && - scanline.startX <= scanline.endX && - !gameModel.unpickable) { + if (mouseX >= scanline.startX >> 8 && mouseX <= scanline.endX >> 8 && scanline.startX <= scanline.endX + && !gameModel.unpickable) { mousePicker.add(gameModel, faceId); } } } - private void rasterize(Canvas canvas, int numFaces, int vertexX[], int vertexY[], int vertexZ[], int textureId, Model gameModel) { + private void rasterize(Canvas canvas, int numFaces, int vertexX[], int vertexY[], int vertexZ[], int textureId, + Model gameModel) { if (textureId == -2) { // Transparent @@ -1211,20 +1204,9 @@ private void rasterize(Canvas canvas, int numFaces, int vertexX[], int vertexY[] int l17 = clipX; k20 = l17 - scanlineStartX; } - canvas.renderScanline_LargeTranslucentTexture( - tex.pixels, - 0, - 0, - l9 + k14 * scanlineStartX, - k11 + i15 * scanlineStartX, - i13 + k15 * scanlineStartX, - k10, - i12, - k13, - k20, - i17 + scanlineStartX, - i22, - k23 << 2); + canvas.renderScanline_LargeTranslucentTexture(tex.pixels, 0, 0, l9 + k14 * scanlineStartX, + k11 + i15 * scanlineStartX, i13 + k15 * scanlineStartX, k10, i12, k13, k20, + i17 + scanlineStartX, i22, k23 << 2); l9 += i11; k11 += k12; i13 += i14; @@ -1262,20 +1244,9 @@ private void rasterize(Canvas canvas, int numFaces, int vertexX[], int vertexY[] int j18 = clipX; l20 = j18 - scanlineStartX; } - canvas.renderScanline_LargeTexture( - tex.pixels, - 0, - 0, - l9 + k14 * scanlineStartX, - k11 + i15 * scanlineStartX, - i13 + k15 * scanlineStartX, - k10, - i12, - k13, - l20, - i17 + scanlineStartX, - j22, - l23 << 2); + canvas.renderScanline_LargeTexture(tex.pixels, 0, 0, l9 + k14 * scanlineStartX, + k11 + i15 * scanlineStartX, i13 + k15 * scanlineStartX, k10, i12, k13, l20, + i17 + scanlineStartX, j22, l23 << 2); l9 += i11; k11 += k12; i13 += i14; @@ -1312,21 +1283,9 @@ private void rasterize(Canvas canvas, int numFaces, int vertexX[], int vertexY[] int l18 = clipX; i21 = l18 - scanlineStartX; } - canvas.renderScanline_LargeTextureWithTransparency( - 0, - 0, - 0, - tex.pixels, - l9 + k14 * scanlineStartX, - k11 + i15 * scanlineStartX, - i13 + k15 * scanlineStartX, - k10, - i12, - k13, - i21, - i17 + scanlineStartX, - k22, - i24); + canvas.renderScanline_LargeTextureWithTransparency(0, 0, 0, tex.pixels, + l9 + k14 * scanlineStartX, k11 + i15 * scanlineStartX, i13 + k15 * scanlineStartX, k10, + i12, k13, i21, i17 + scanlineStartX, k22, i24); l9 += i11; k11 += k12; i13 += i14; @@ -1387,20 +1346,9 @@ private void rasterize(Canvas canvas, int numFaces, int vertexX[], int vertexY[] int j19 = clipX; j21 = j19 - scanlineStartX; } - canvas.renderScanline_SmallTranslucentTexture( - tex.pixels, - 0, - 0, - i10 + l14 * scanlineStartX, - l11 + j15 * scanlineStartX, - j13 + l15 * scanlineStartX, - l10, - j12, - l13, - j21, - j17 + scanlineStartX, - l22, - j24); + canvas.renderScanline_SmallTranslucentTexture(tex.pixels, 0, 0, i10 + l14 * scanlineStartX, + l11 + j15 * scanlineStartX, j13 + l15 * scanlineStartX, l10, j12, l13, j21, + j17 + scanlineStartX, l22, j24); i10 += j11; l11 += l12; j13 += j14; @@ -1441,20 +1389,9 @@ private void rasterize(Canvas canvas, int numFaces, int vertexX[], int vertexY[] k21 = l19 - scanlineStartX; } - canvas.renderScanline_SmallTexture( - tex.pixels, - 0, - 0, - i10 + l14 * scanlineStartX, - l11 + j15 * scanlineStartX, - j13 + l15 * scanlineStartX, - l10, - j12, - l13, - k21, - j17 + scanlineStartX, - i23, - k24); + canvas.renderScanline_SmallTexture(tex.pixels, 0, 0, i10 + l14 * scanlineStartX, + l11 + j15 * scanlineStartX, j13 + l15 * scanlineStartX, l10, j12, l13, k21, + j17 + scanlineStartX, i23, k24); i10 += j11; l11 += l12; @@ -1490,18 +1427,9 @@ private void rasterize(Canvas canvas, int numFaces, int vertexX[], int vertexY[] int j20 = clipX; l21 = j20 - scanlineStartX; } - canvas.renderScanline_SmallTextureWithTransparency( - tex.pixels, - i10 + l14 * scanlineStartX, - l11 + j15 * scanlineStartX, - j13 + l15 * scanlineStartX, - l10, - j12, - l13, - l21, - j17 + scanlineStartX, - j23, - l24); + canvas.renderScanline_SmallTextureWithTransparency(tex.pixels, i10 + l14 * scanlineStartX, + l11 + j15 * scanlineStartX, j13 + l15 * scanlineStartX, l10, j12, l13, l21, + j17 + scanlineStartX, j23, l24); i10 += j11; l11 += l12; j13 += j14; @@ -1559,12 +1487,8 @@ private void rasterize(Canvas canvas, int numFaces, int vertexX[], int vertexY[] if (k4 > clipX) { length = clipX - scanlineStartX; } - canvas.renderScanline_TranslucentGradient( - length, - rowStart + scanlineStartX, - currentGradientRamps, - gradientIndex, - stride * 4); + canvas.renderScanline_TranslucentGradient(length, rowStart + scanlineStartX, currentGradientRamps, + gradientIndex, stride * 4); rowStart += width; } } @@ -1592,11 +1516,7 @@ private void rasterize(Canvas canvas, int numFaces, int vertexX[], int vertexY[] length = l5 - scanlineStartX; } - canvas.renderScanline_Gradient( - length, - rowStart + scanlineStartX, - currentGradientRamps, - gradientIndex, + canvas.renderScanline_Gradient(length, rowStart + scanlineStartX, currentGradientRamps, gradientIndex, stride * 4); rowStart += width; @@ -1632,7 +1552,8 @@ private void initialisePolygon3d(int i) { } gameModel.faceCameraNormalScale[face] = faceCameraNormalScale; - gameModel.faceCameraNormalMagnitude[face] = (int) (normalMagnitude * Math.sqrt(k3 * k3 + l3 * l3 + i4 * i4)); + gameModel.faceCameraNormalMagnitude[face] = (int) (normalMagnitude + * Math.sqrt(k3 * k3 + l3 * l3 + i4 * i4)); } else { k3 >>= faceCameraNormalScale; l3 >>= faceCameraNormalScale; diff --git a/rsc-client/src/client/game/scene/Model.java b/rsc-client/src/client/game/scene/Model.java index 24147ea..24f34e1 100644 --- a/rsc-client/src/client/game/scene/Model.java +++ b/rsc-client/src/client/game/scene/Model.java @@ -21,9 +21,7 @@ public class Model { private static final int DEFAULT_SCALE = 256; public enum TransformState { - CLEAN, - PENDING, - BILLBOARD + CLEAN, PENDING, BILLBOARD } private static int sine9[] = new int[512]; @@ -276,8 +274,7 @@ public Model(String path) { } /** - * Copies the Model at the given index of the given array, and sets some - * flags. + * Copies the Model at the given index of the given array, and sets some flags. * * @param models * @param i @@ -325,13 +322,7 @@ public Model(int maxVertices, int maxFaces) { * @param unpickable * @param projected */ - public Model( - int maxVertices, - int maxFaces, - boolean autoCommit, - boolean isolated, - boolean unlit, - boolean unpickable, + public Model(int maxVertices, int maxFaces, boolean autoCommit, boolean isolated, boolean unlit, boolean unpickable, boolean projected) { this.autoCommit = autoCommit; this.isolated = isolated; @@ -448,16 +439,11 @@ public void merge(Model models[], int numModels) { int vertices[] = gameModel.faceVertices[faceId]; for (int vertId = 0; vertId < gameModel.numVerticesPerFace[faceId]; vertId++) { - faces[vertId] = addUniqueVertex( - gameModel.vertices[vertices[vertId]].x, - gameModel.vertices[vertices[vertId]].y, - gameModel.vertices[vertices[vertId]].z); + faces[vertId] = addUniqueVertex(gameModel.vertices[vertices[vertId]].x, + gameModel.vertices[vertices[vertId]].y, gameModel.vertices[vertices[vertId]].z); } - int faceIndex = addFace( - gameModel.numVerticesPerFace[faceId], - faces, - gameModel.faceFillFront[faceId], + int faceIndex = addFace(gameModel.numVerticesPerFace[faceId], faces, gameModel.faceFillFront[faceId], gameModel.faceFillBack[faceId]); faceIntensity[faceIndex] = gameModel.faceIntensity[faceId]; @@ -561,10 +547,7 @@ public Model[] split(int pieceDx, int pieceDz, int rows, int count, int maxVerti if (numVerticesInPiece[i] > maxVertices) { numVerticesInPiece[i] = maxVertices; } - models[i] = new Model( - numVerticesInPiece[i], - numFacesInPiece[i], - true, true, true, unpickable, true); + models[i] = new Model(numVerticesInPiece[i], numFacesInPiece[i], true, true, true, unpickable, true); models[i].lightDiffuse = lightDiffuse; models[i].lightAmbience = lightAmbience; } @@ -590,17 +573,11 @@ public Model[] split(int pieceDx, int pieceDz, int rows, int count, int maxVerti return models; } - public void copyModelData( - Model gameModel, - int srcVertices[], - int count, - int faceId) { + public void copyModelData(Model gameModel, int srcVertices[], int count, int faceId) { int destVertices[] = new int[count]; for (int i = 0; i < count; i++) { - int l = destVertices[i] = gameModel.addUniqueVertex( - vertices[srcVertices[i]].x, - vertices[srcVertices[i]].y, + int l = destVertices[i] = gameModel.addUniqueVertex(vertices[srcVertices[i]].x, vertices[srcVertices[i]].y, vertices[srcVertices[i]].z); gameModel.vertexIntensity[l] = vertexIntensity[srcVertices[i]]; gameModel.vertexAmbience[l] = vertexAmbience[srcVertices[i]]; @@ -615,12 +592,7 @@ public void copyModelData( gameModel.faceCameraNormalMagnitude[nextIndex] = faceCameraNormalMagnitude[faceId]; } - public void setLighting( - boolean useGouraud, - int ambient, - int diffuse, - int lightDirectionX, - int lightDirectionY, + public void setLighting(boolean useGouraud, int ambient, int diffuse, int lightDirectionX, int lightDirectionY, int lightDirectionZ) { lightAmbience = 256 - ambient * 4; @@ -644,12 +616,7 @@ public void setLighting( light(); } - public void setLighting( - int ambient, - int diffuse, - int lightDirectionX, - int lightDirectionY, - int lightDirectionZ) { + public void setLighting(int ambient, int diffuse, int lightDirectionX, int lightDirectionY, int lightDirectionZ) { lightAmbience = 256 - ambient * 4; lightDiffuse = (64 - diffuse) * 16 + 128; @@ -664,10 +631,7 @@ public void setLighting( light(); } - public void setLighting( - int lightDirectionX, - int lightDirectionY, - int lightDirectionZ) { + public void setLighting(int lightDirectionX, int lightDirectionY, int lightDirectionZ) { if (unlit) { return; @@ -708,8 +672,7 @@ public void setTranslate(int translateX, int translateY, int translateZ) { } private void determineTransformType() { - if (shearXY != 256 || shearXZ != 256 || shearYX != 256 || shearYZ != 256 - || shearZX != 256 || shearZY != 256) { + if (shearXY != 256 || shearXZ != 256 || shearYX != 256 || shearYZ != 256 || shearZX != 256 || shearZY != 256) { transformType = 4; return; } @@ -765,13 +728,7 @@ private void applyRotation(int rotX, int rotY, int rotZ) { } - private void applyShear( - int xy, - int xz, - int yx, - int yz, - int zx, - int zy) { + private void applyShear(int xy, int xz, int yx, int yz, int zx, int zy) { for (int i = 0; i < numVertices; i++) { if (xy != 0) { verticesTransformed[i].x += verticesTransformed[i].y * xy >> 8; @@ -880,9 +837,7 @@ public void light() { int i = lightDiffuse * VectorUtils.magnitude(lightDirection) >> 8; for (int j = 0; j < numFaces; j++) { if (faceIntensity[j] != USE_GOURAUD_LIGHTING) { - faceIntensity[j] = - (faceNormals[j].x * lightDirection.x - + faceNormals[j].y * lightDirection.y + faceIntensity[j] = (faceNormals[j].x * lightDirection.x + faceNormals[j].y * lightDirection.y + faceNormals[j].z * lightDirection.z) / i; } } @@ -913,11 +868,8 @@ public void light() { for (int j1 = 0; j1 < numVertices; j1++) { if (normalMagnitude[j1] > 0) { - vertexIntensity[j1] = - (normalX[j1] * lightDirection.x - + normalY[j1] * lightDirection.y - + normalZ[j1] * lightDirection.z) - / (i * normalMagnitude[j1]); + vertexIntensity[j1] = (normalX[j1] * lightDirection.x + normalY[j1] * lightDirection.y + + normalZ[j1] * lightDirection.z) / (i * normalMagnitude[j1]); } } } @@ -945,21 +897,13 @@ public void relight() { int normalY = (bZ * cX) - (cZ * bX); int normalZ; - for (normalZ = bX * cY - cX * bY; - normalX > 8192 - || normalY > 8192 - || normalZ > 8192 - || normalX < -8192 - || normalY < -8192 - || normalZ < -8192; normalZ >>= 1) { + for (normalZ = bX * cY - cX * bY; normalX > 8192 || normalY > 8192 || normalZ > 8192 || normalX < -8192 + || normalY < -8192 || normalZ < -8192; normalZ >>= 1) { normalX >>= 1; normalY >>= 1; } - int normalMagnitude = (int) (256D * Math.sqrt( - normalX * normalX - + normalY * normalY - + normalZ * normalZ)); + int normalMagnitude = (int) (256D * Math.sqrt(normalX * normalX + normalY * normalY + normalZ * normalZ)); if (normalMagnitude <= 0) { normalMagnitude = 1; } @@ -996,8 +940,7 @@ private void applyTransform() { scale(scale.x, scale.y, scale.z); } if (transformType >= 4) { - applyShear( - shearXY, shearXZ, shearYX, shearYZ, shearZX, shearZY); + applyShear(shearXY, shearXZ, shearYX, shearYZ, shearZX, shearZY); } if (transformType >= 1) { applyTranslate(translate.x, translate.y, translate.z); @@ -1009,12 +952,8 @@ private void applyTransform() { public void project(Camera camera, int viewDistance, int clipNear) { applyTransform(); - if (z1 > camera.getFrustumNearZ() || - z2 < camera.getFrustumFarZ() || - x1 > camera.getFrustumMinX() || - x2 < camera.getFrustumMaxX() || - y1 > camera.getFrustumMaxY() || - y2 < camera.getFrustumMinY()) { + if (z1 > camera.getFrustumNearZ() || z2 < camera.getFrustumFarZ() || x1 > camera.getFrustumMinX() + || x2 < camera.getFrustumMaxX() || y1 > camera.getFrustumMaxY() || y2 < camera.getFrustumMinY()) { visible = false; return; } @@ -1093,11 +1032,7 @@ public Model copy() { return gameModel; } - public Model copy( - boolean autoCommit, - boolean isolated, - boolean unlit, - boolean unpickable) { + public Model copy(boolean autoCommit, boolean isolated, boolean unlit, boolean unpickable) { Model models[] = new Model[1]; models[0] = this; Model gameModel = new Model(models, 1, autoCommit, isolated, unlit, unpickable); diff --git a/rsc-client/src/client/game/scene/Scene.java b/rsc-client/src/client/game/scene/Scene.java index 7d1ee5d..76d1952 100644 --- a/rsc-client/src/client/game/scene/Scene.java +++ b/rsc-client/src/client/game/scene/Scene.java @@ -22,18 +22,18 @@ public class Scene { * Fog "density". */ public int fogZFalloff = 1; - + public int fogZDistance = 2100 + (Camera.DEFAULT_HEIGHT * 2); - + public Scene() { for (int l = 0; l < spriteEntities.length; l++) { spriteEntities[l] = new SpriteEntity(); } sprites = new Model(MAX_SPRITES * 2, MAX_SPRITES); camera = new Camera(); - + camera.set(0, 0, 0, 912, 0, 0, 2000); - + setLight(-50, -10, -50); } @@ -83,13 +83,8 @@ public void reduceSprites(int i) { public int addSpriteEntity(SpriteEntity spriteEntity, int tag) { spriteEntities[numSprites] = spriteEntity; - int v1 = sprites.addVertex( - spriteEntity.getX(), - spriteEntity.getY(), - spriteEntity.getZ()); - int v2 = sprites.addVertex( - spriteEntity.getX(), - spriteEntity.getZ() - spriteEntity.getHeight(), + int v1 = sprites.addVertex(spriteEntity.getX(), spriteEntity.getY(), spriteEntity.getZ()); + int v2 = sprites.addVertex(spriteEntity.getX(), spriteEntity.getZ() - spriteEntity.getHeight(), spriteEntity.getY()); int vertices[] = { v1, v2 }; sprites.addFace(2, vertices, 0, 0); @@ -121,19 +116,19 @@ public void setLight(int i, int j, int distX, int distY, int distZ) { public Model getSprites() { return sprites; } - + public Camera getCamera() { return camera; } - + public int getNumModels() { return numModels; } - + public Model[] getModels() { return models; } - + public SpriteEntity[] getSpriteEntities() { return spriteEntities; } diff --git a/rsc-client/src/client/game/scene/SpriteEntity.java b/rsc-client/src/client/game/scene/SpriteEntity.java index b508cb9..31f8bf2 100644 --- a/rsc-client/src/client/game/scene/SpriteEntity.java +++ b/rsc-client/src/client/game/scene/SpriteEntity.java @@ -14,19 +14,19 @@ public class SpriteEntity { private int width; private int height; private int translateX; - + public int getId() { return id; } - + public int getX() { return x; } - + public int getY() { return y; } - + public int getZ() { return z; } @@ -34,13 +34,13 @@ public int getZ() { public int getWidth() { return width; } - + public int getHeight() { return height; } - + public int getTranslateX() { return translateX; } - + } diff --git a/rsc-client/src/client/game/world/Door.java b/rsc-client/src/client/game/world/Door.java index 037ec3d..44abf8d 100644 --- a/rsc-client/src/client/game/world/Door.java +++ b/rsc-client/src/client/game/world/Door.java @@ -6,7 +6,7 @@ public class Door { private World world; - + private int x; private int z; private int orientation; @@ -19,7 +19,7 @@ public Door(World world, int x, int z, int orientation, int id, int entityId) { this.z = z; this.orientation = orientation; this.id = id; - + model = ModelUtils.createDoor(this, world, entityId); } @@ -28,23 +28,23 @@ public void move(int dx, int dz) { z += dz; model = ModelUtils.createDoor(this, world, model.entityId); } - + public int getX() { return x; } - + public int getZ() { return z; } - + public int getOrientation() { return orientation; } - + public int getId() { return id; } - + public Model getModel() { return model; } diff --git a/rsc-client/src/client/game/world/GameObject.java b/rsc-client/src/client/game/world/GameObject.java index 1c57279..98c330e 100644 --- a/rsc-client/src/client/game/world/GameObject.java +++ b/rsc-client/src/client/game/world/GameObject.java @@ -6,13 +6,13 @@ public class GameObject { private World world; - + private int x; private int z; private int type; private int id; private Model model; - + public GameObject(World world, int x, int z, int type, int id) { this.world = world; this.x = x; @@ -20,23 +20,23 @@ public GameObject(World world, int x, int z, int type, int id) { this.type = type; this.id = id; } - + public int getX() { return x; } - + public int getZ() { return z; } - + public int getType() { return type; } - + public int getId() { return id; } - + public Model getModel() { return model; } @@ -45,10 +45,10 @@ public void move(int dx, int dz) { x += dx; z += dz; - + int width; int height; - + if (id == 0 || id == 4) { // Special case objects have width and height swapped? width = Resources.getObjectDef(type).getWidth(); @@ -57,19 +57,19 @@ public void move(int dx, int dz) { height = Resources.getObjectDef(type).getWidth(); width = Resources.getObjectDef(type).getHeight(); } - + int tileX = ((x + x + width) * World.TILE_WIDTH) / 2; int tileZ = ((z + z + height) * World.TILE_DEPTH) / 2; - + // Add new models if (world.containsTileRelativeToOrigin(x, z)) { model.setTranslate(tileX, -world.getAveragedElevation(tileX, tileZ), tileZ); - + if (type == 74) { // Special case object needs to be higher? model.translate(0, -480, 0); } } } - + } diff --git a/rsc-client/src/client/game/world/World.java b/rsc-client/src/client/game/world/World.java index 4d93e42..0ff4641 100644 --- a/rsc-client/src/client/game/world/World.java +++ b/rsc-client/src/client/game/world/World.java @@ -82,7 +82,8 @@ public class World { /** * Tile position corresponding to a given face of the terrain model. * - *

The index is given by the faceTag of the selected face. + *

+ * The index is given by the faceTag of the selected face. */ private int[] tileXForFace = new int[NUM_TERRAIN_FACES]; private int[] tileZForFace = new int[NUM_TERRAIN_FACES]; @@ -90,7 +91,8 @@ public class World { /** * Currently-loaded Sectors. * - *

When we load sector (x, z) we end up with the following: + *

+ * When we load sector (x, z) we end up with the following: * *

      *  sectors[0] = (x - 1, z - 1)
@@ -209,8 +211,7 @@ public int getAveragedElevation(int tileX, int tileZ) {
         int i1 = tileX & 0x7f;
         int j1 = tileZ & 0x7f;
 
-        if (x < 0 || z < 0 ||
-                x >= World.NUM_TILES_X - 1 || z >= World.NUM_TILES_Z - 1) {
+        if (x < 0 || z < 0 || x >= World.NUM_TILES_X - 1 || z >= World.NUM_TILES_Z - 1) {
             return 0;
         }
 
@@ -347,13 +348,11 @@ public int getOriginZ() {
     }
 
     public boolean containsTile(int tileX, int tileZ) {
-        return tileX > mapBoundaryX1 && tileX < mapBoundaryX2 &&
-                tileZ > mapBoundaryZ1 && tileZ < mapBoundaryZ2;
+        return tileX > mapBoundaryX1 && tileX < mapBoundaryX2 && tileZ > mapBoundaryZ1 && tileZ < mapBoundaryZ2;
     }
 
     public boolean containsTileRelativeToOrigin(int tileX, int tileZ) {
-        return tileX >= 0 && tileZ >= 0 &&
-                tileX < NUM_TILES_X && tileZ < NUM_TILES_Z;
+        return tileX >= 0 && tileZ >= 0 && tileX < NUM_TILES_X && tileZ < NUM_TILES_Z;
     }
 
     public int getNumDoors() {
diff --git a/rsc-client/src/client/game/world/WorldLoader.java b/rsc-client/src/client/game/world/WorldLoader.java
index c13a8f9..e902075 100644
--- a/rsc-client/src/client/game/world/WorldLoader.java
+++ b/rsc-client/src/client/game/world/WorldLoader.java
@@ -17,14 +17,8 @@ public class WorldLoader {
     /**
      * Model used when loading sectors.
      */
-    private Model tmpModel = new Model(
-            World.NUM_TERRAIN_FACES + 256,
-            World.NUM_TERRAIN_FACES + 256,
-            true,
-            true,
-            false,
-            false,
-            true);
+    private Model tmpModel = new Model(World.NUM_TERRAIN_FACES + 256, World.NUM_TERRAIN_FACES + 256, true, true, false,
+            false, true);
 
     /**
      * Ground colour palette.
@@ -50,28 +44,16 @@ public class WorldLoader {
         for (int i = 0; i < 64; i++) {
 
             // Pale Grass / Snow
-            GROUND_COLOURS[i] = DataUtils.rgbToInt(
-                    255 - i * 4,
-                    255 - (int) (i * 1.75),
-                    255 - i * 4);
+            GROUND_COLOURS[i] = DataUtils.rgbToInt(255 - i * 4, 255 - (int) (i * 1.75), 255 - i * 4);
 
             // Grass
-            GROUND_COLOURS[i + 64] = DataUtils.rgbToInt(
-                    i * 3,
-                    144,
-                    0);
+            GROUND_COLOURS[i + 64] = DataUtils.rgbToInt(i * 3, 144, 0);
 
             // Sand
-            GROUND_COLOURS[i + 128] = DataUtils.rgbToInt(
-                    192 - (int) (i * 1.5),
-                    144 - (int) (i * 1.5),
-                    0);
+            GROUND_COLOURS[i + 128] = DataUtils.rgbToInt(192 - (int) (i * 1.5), 144 - (int) (i * 1.5), 0);
 
             // Dark Grass / Mud
-            GROUND_COLOURS[i + 192] = DataUtils.rgbToInt(
-                    96 - (int) (i * 1.5),
-                    48 + (int) (i * 1.5),
-                    0);
+            GROUND_COLOURS[i + 192] = DataUtils.rgbToInt(96 - (int) (i * 1.5), 48 + (int) (i * 1.5), 0);
         }
     }
 
@@ -138,8 +120,7 @@ public void loadSector(int sectorX, int sectorZ) {
      */
     private void loadRequiredLayers(int sectorX, int sectorZ, int currentLayer) {
 
-        System.out.println("Loading sector: " + sectorX + ", " + sectorZ +
-                " (" + currentLayer + ")");
+        System.out.println("Loading sector: " + sectorX + ", " + sectorZ + " (" + currentLayer + ")");
 
         loadLayer(sectorX, sectorZ, currentLayer, true);
 
@@ -181,24 +162,22 @@ private void loadLayer(int sectorX, int sectorZ, int layer, boolean isCurrentLay
                     int elevation = -world.getGroundElevation(x, z);
 
                     // Flatten water under bridges
-                    if (world.getGroundTextureOverlay(x, z) > 0
-                            && Resources.getTileDef(world.getGroundTextureOverlay(x, z) - 1).getType() == TileDef.TYPE_BRIDGE) {
+                    if (world.getGroundTextureOverlay(x, z) > 0 && Resources
+                            .getTileDef(world.getGroundTextureOverlay(x, z) - 1).getType() == TileDef.TYPE_BRIDGE) {
                         elevation = 0;
-                    } else if (world.getGroundTextureOverlay(x - 1, z) > 0
-                            && Resources.getTileDef(world.getGroundTextureOverlay(x - 1, z) - 1).getType() == TileDef.TYPE_BRIDGE) {
+                    } else if (world.getGroundTextureOverlay(x - 1, z) > 0 && Resources
+                            .getTileDef(world.getGroundTextureOverlay(x - 1, z) - 1).getType() == TileDef.TYPE_BRIDGE) {
                         elevation = 0;
-                    } else if (world.getGroundTextureOverlay(x, z - 1) > 0
-                            && Resources.getTileDef(world.getGroundTextureOverlay(x, z - 1) - 1).getType() == TileDef.TYPE_BRIDGE) {
+                    } else if (world.getGroundTextureOverlay(x, z - 1) > 0 && Resources
+                            .getTileDef(world.getGroundTextureOverlay(x, z - 1) - 1).getType() == TileDef.TYPE_BRIDGE) {
                         elevation = 0;
-                    } else if (world.getGroundTextureOverlay(x - 1, z - 1) > 0 &&
-                            Resources.getTileDef(world.getGroundTextureOverlay(x - 1, z - 1) - 1).getType() == TileDef.TYPE_BRIDGE) {
+                    } else if (world.getGroundTextureOverlay(x - 1, z - 1) > 0
+                            && Resources.getTileDef(world.getGroundTextureOverlay(x - 1, z - 1) - 1)
+                                    .getType() == TileDef.TYPE_BRIDGE) {
                         elevation = 0;
                     }
 
-                    int vertexId = tmpModel.addUniqueVertex(
-                            x * World.TILE_WIDTH,
-                            elevation,
-                            z * World.TILE_DEPTH);
+                    int vertexId = tmpModel.addUniqueVertex(x * World.TILE_WIDTH, elevation, z * World.TILE_DEPTH);
 
                     // Randomise vertex ambience
                     int ambience = (int) (Math.random() * 10D) - 5;
@@ -262,7 +241,7 @@ && getOverlayIfRequired(x, z + 1, groundColour2) != 0xbc614e) {
                                 }
                             }
 
-                        // Create smooth diagonal lines for road / water edges, etc.
+                            // Create smooth diagonal lines for road / water edges, etc.
                         } else if (tileType1 != 2 || getDiagonalWalls(x, z) > 0 && getDiagonalWalls(x, z) < 24000) {
                             if (getTileType(x - 1, z) != tileType2 && getTileType(x, z - 1) != tileType2) {
                                 groundColour = groundColour2;
@@ -337,8 +316,8 @@ && getOverlayIfRequired(x, z + 1, groundColour2) != 0xbc614e) {
             for (int x = 1; x < World.NUM_TILES_X - 1; x++) {
                 for (int z = 1; z < World.NUM_TILES_Z - 1; z++) {
 
-                    if (world.getGroundTextureOverlay(x, z) > 0
-                            && Resources.getTileDef(world.getGroundTextureOverlay(x, z) - 1).getType() == TileDef.TYPE_BRIDGE) {
+                    if (world.getGroundTextureOverlay(x, z) > 0 && Resources
+                            .getTileDef(world.getGroundTextureOverlay(x, z) - 1).getType() == TileDef.TYPE_BRIDGE) {
                         int l7 = Resources.getTileDef(world.getGroundTextureOverlay(x, z) - 1).getColour();
                         int j10 = tmpModel.addUniqueVertex(x * 128, -world.getGroundElevation(x, z), z * 128);
                         int l12 = tmpModel.addUniqueVertex((x + 1) * 128, -world.getGroundElevation(x + 1, z), z * 128);
@@ -352,56 +331,68 @@ && getOverlayIfRequired(x, z + 1, groundColour2) != 0xbc614e) {
 
                     } else if (world.getGroundTextureOverlay(x, z) == 0
                             || Resources.getTileDef(world.getGroundTextureOverlay(x, z) - 1).getType() != 3) {
-                        if (world.getGroundTextureOverlay(x, z + 1) > 0 && Resources
-                                .getTileDef(world.getGroundTextureOverlay(x, z + 1) - 1).getType() == TileDef.TYPE_BRIDGE) {
+                        if (world.getGroundTextureOverlay(x, z + 1) > 0
+                                && Resources.getTileDef(world.getGroundTextureOverlay(x, z + 1) - 1)
+                                        .getType() == TileDef.TYPE_BRIDGE) {
                             int i8 = Resources.getTileDef(world.getGroundTextureOverlay(x, z + 1) - 1).getColour();
                             int k10 = tmpModel.addUniqueVertex(x * 128, -world.getGroundElevation(x, z), z * 128);
-                            int i13 = tmpModel.addUniqueVertex((x + 1) * 128, -world.getGroundElevation(x + 1, z), z * 128);
+                            int i13 = tmpModel.addUniqueVertex((x + 1) * 128, -world.getGroundElevation(x + 1, z),
+                                    z * 128);
                             int j15 = tmpModel.addUniqueVertex((x + 1) * 128, -world.getGroundElevation(x + 1, z + 1),
                                     (z + 1) * 128);
-                            int k17 = tmpModel.addUniqueVertex(x * 128, -world.getGroundElevation(x, z + 1), (z + 1) * 128);
+                            int k17 = tmpModel.addUniqueVertex(x * 128, -world.getGroundElevation(x, z + 1),
+                                    (z + 1) * 128);
                             int ai3[] = { k10, i13, j15, k17 };
                             int j20 = tmpModel.addFace(4, ai3, i8, 0xbc614e);
                             world.setTilePosForFace(j20, x, z);
                             tmpModel.faceTag[j20] = 0x30d40 + j20;
                         }
 
-                        if (world.getGroundTextureOverlay(x, z - 1) > 0 && Resources
-                                .getTileDef(world.getGroundTextureOverlay(x, z - 1) - 1).getType() == TileDef.TYPE_BRIDGE) {
+                        if (world.getGroundTextureOverlay(x, z - 1) > 0
+                                && Resources.getTileDef(world.getGroundTextureOverlay(x, z - 1) - 1)
+                                        .getType() == TileDef.TYPE_BRIDGE) {
                             int j8 = Resources.getTileDef(world.getGroundTextureOverlay(x, z - 1) - 1).getColour();
                             int l10 = tmpModel.addUniqueVertex(x * 128, -world.getGroundElevation(x, z), z * 128);
-                            int j13 = tmpModel.addUniqueVertex((x + 1) * 128, -world.getGroundElevation(x + 1, z), z * 128);
+                            int j13 = tmpModel.addUniqueVertex((x + 1) * 128, -world.getGroundElevation(x + 1, z),
+                                    z * 128);
                             int k15 = tmpModel.addUniqueVertex((x + 1) * 128, -world.getGroundElevation(x + 1, z + 1),
                                     (z + 1) * 128);
-                            int l17 = tmpModel.addUniqueVertex(x * 128, -world.getGroundElevation(x, z + 1), (z + 1) * 128);
+                            int l17 = tmpModel.addUniqueVertex(x * 128, -world.getGroundElevation(x, z + 1),
+                                    (z + 1) * 128);
                             int ai4[] = { l10, j13, k15, l17 };
                             int k20 = tmpModel.addFace(4, ai4, j8, 0xbc614e);
                             world.setTilePosForFace(k20, x, z);
                             tmpModel.faceTag[k20] = 0x30d40 + k20;
                         }
 
-                        if (world.getGroundTextureOverlay(x + 1, z) > 0 && Resources
-                                .getTileDef(world.getGroundTextureOverlay(x + 1, z) - 1).getType() == TileDef.TYPE_BRIDGE) {
+                        if (world.getGroundTextureOverlay(x + 1, z) > 0
+                                && Resources.getTileDef(world.getGroundTextureOverlay(x + 1, z) - 1)
+                                        .getType() == TileDef.TYPE_BRIDGE) {
                             int k8 = Resources.getTileDef(world.getGroundTextureOverlay(x + 1, z) - 1).getColour();
                             int i11 = tmpModel.addUniqueVertex(x * 128, -world.getGroundElevation(x, z), z * 128);
-                            int k13 = tmpModel.addUniqueVertex((x + 1) * 128, -world.getGroundElevation(x + 1, z), z * 128);
+                            int k13 = tmpModel.addUniqueVertex((x + 1) * 128, -world.getGroundElevation(x + 1, z),
+                                    z * 128);
                             int l15 = tmpModel.addUniqueVertex((x + 1) * 128, -world.getGroundElevation(x + 1, z + 1),
                                     (z + 1) * 128);
-                            int i18 = tmpModel.addUniqueVertex(x * 128, -world.getGroundElevation(x, z + 1), (z + 1) * 128);
+                            int i18 = tmpModel.addUniqueVertex(x * 128, -world.getGroundElevation(x, z + 1),
+                                    (z + 1) * 128);
                             int ai5[] = { i11, k13, l15, i18 };
                             int l20 = tmpModel.addFace(4, ai5, k8, 0xbc614e);
                             world.setTilePosForFace(l20, x, z);
                             tmpModel.faceTag[l20] = 0x30d40 + l20;
                         }
 
-                        if (world.getGroundTextureOverlay(x - 1, z) > 0 && Resources
-                                .getTileDef(world.getGroundTextureOverlay(x - 1, z) - 1).getType() == TileDef.TYPE_BRIDGE) {
+                        if (world.getGroundTextureOverlay(x - 1, z) > 0
+                                && Resources.getTileDef(world.getGroundTextureOverlay(x - 1, z) - 1)
+                                        .getType() == TileDef.TYPE_BRIDGE) {
                             int l8 = Resources.getTileDef(world.getGroundTextureOverlay(x - 1, z) - 1).getColour();
                             int j11 = tmpModel.addUniqueVertex(x * 128, -world.getGroundElevation(x, z), z * 128);
-                            int l13 = tmpModel.addUniqueVertex((x + 1) * 128, -world.getGroundElevation(x + 1, z), z * 128);
+                            int l13 = tmpModel.addUniqueVertex((x + 1) * 128, -world.getGroundElevation(x + 1, z),
+                                    z * 128);
                             int i16 = tmpModel.addUniqueVertex((x + 1) * 128, -world.getGroundElevation(x + 1, z + 1),
                                     (z + 1) * 128);
-                            int j18 = tmpModel.addUniqueVertex(x * 128, -world.getGroundElevation(x, z + 1), (z + 1) * 128);
+                            int j18 = tmpModel.addUniqueVertex(x * 128, -world.getGroundElevation(x, z + 1),
+                                    (z + 1) * 128);
                             int ai6[] = { j11, l13, i16, j18 };
                             int i21 = tmpModel.addFace(4, ai6, l8, 0xbc614e);
                             world.setTilePosForFace(i21, x, z);
@@ -521,26 +512,22 @@ && getOverlayIfRequired(x, z + 1, groundColour2) != 0xbc614e) {
                     if (j24 < 0x13880) {
                         world.setElevation(l11, i14, l23);
                     } else {
-                        world.setElevation(l11, i14,
-                                world.getElevation(l11, i14) - 0x13880);
+                        world.setElevation(l11, i14, world.getElevation(l11, i14) - 0x13880);
                     }
                     if (l24 < 0x13880) {
                         world.setElevation(j16, k18, l23);
                     } else {
-                        world.setElevation(j16, k18,
-                                world.getElevation(j16, k18) - 0x13880);
+                        world.setElevation(j16, k18, world.getElevation(j16, k18) - 0x13880);
                     }
                     if (j25 < 0x13880) {
                         world.setElevation(j19, j21, l23);
                     } else {
-                        world.setElevation(j19, j21,
-                                world.getElevation(j19, j21) - 0x13880);
+                        world.setElevation(j19, j21, world.getElevation(j19, j21) - 0x13880);
                     }
                     if (l25 < 0x13880) {
                         world.setElevation(l22, j23, l23);
                     } else {
-                        world.setElevation(l22, j23,
-                                world.getElevation(l22, j23) - 0x13880);
+                        world.setElevation(l22, j23, world.getElevation(l22, j23) - 0x13880);
                     }
                 }
             }
@@ -861,28 +848,22 @@ private int getOverlayIfRequired(int x, int z, int underlay) {
     }
 
     private boolean isCentreRoof(int x, int z) {
-        return getRoofTexture(x, z) > 0 &&
-                getRoofTexture(x - 1, z) > 0 &&
-                getRoofTexture(x - 1, z - 1) > 0 &&
-                getRoofTexture(x, z - 1) > 0;
+        return getRoofTexture(x, z) > 0 && getRoofTexture(x - 1, z) > 0 && getRoofTexture(x - 1, z - 1) > 0
+                && getRoofTexture(x, z - 1) > 0;
     }
 
     private boolean isCornerRoof(int x, int z) {
-        return getRoofTexture(x, z) > 0 ||
-                getRoofTexture(x - 1, z) > 0 ||
-                getRoofTexture(x - 1, z - 1) > 0 ||
-                getRoofTexture(x, z - 1) > 0;
+        return getRoofTexture(x, z) > 0 || getRoofTexture(x - 1, z) > 0 || getRoofTexture(x - 1, z - 1) > 0
+                || getRoofTexture(x, z - 1) > 0;
     }
 
     private void setDoorElevation(int doorIndex, int x1, int z1, int x2, int z2) {
         int heightIncrement = Resources.getDoorDef(doorIndex).getHeight();
         if (world.getElevation(x1, z1) < 0x13880) {
-            world.setElevation(x1, z1,
-                    world.getElevation(x1, z1) + 0x13880 + heightIncrement);
+            world.setElevation(x1, z1, world.getElevation(x1, z1) + 0x13880 + heightIncrement);
         }
         if (world.getElevation(x2, z2) < 0x13880) {
-            world.setElevation(x2, z2,
-                    world.getElevation(x2, z2) + 0x13880 + heightIncrement);
+            world.setElevation(x2, z2, world.getElevation(x2, z2) + 0x13880 + heightIncrement);
         }
     }
 
diff --git a/rsc-client/src/client/loading/LoadingScreen.java b/rsc-client/src/client/loading/LoadingScreen.java
index b974ec1..7aed9b7 100644
--- a/rsc-client/src/client/loading/LoadingScreen.java
+++ b/rsc-client/src/client/loading/LoadingScreen.java
@@ -19,7 +19,6 @@
 import client.entityhandling.defs.SpellDef;
 import client.entityhandling.defs.TextureDef;
 import client.entityhandling.defs.TileDef;
-import client.login.LoginScreen;
 import client.res.ResourceLoader;
 import client.res.Resources;
 import client.res.Sprite;
@@ -107,7 +106,9 @@ public void continueLoading() {
             updateProgress(100, "Starting game...");
         }
 
-        launcher.changeState(new LoginScreen(launcher));
+        // Finished loading.
+        // Update the launcher state.
+        launcher.onLoaded();
     }
 
     private void generateExperienceTable() {
@@ -124,16 +125,16 @@ private void loadGameData() {
 
         Resources.tileArchive = ResourceLoader.loadZipData(LANDSCAPE_FILENAME);
 
-        Resources.animations  = (AnimationDef[])  ResourceLoader.loadGzipData("Animations.xml.gz");
-        Resources.doors       = (DoorDef[])       ResourceLoader.loadGzipData("Doors.xml.gz");
-        Resources.elevation   = (ElevationDef[])  ResourceLoader.loadGzipData("Elevation.xml.gz");
-        Resources.items       = (ItemDef[])       ResourceLoader.loadGzipData("Items.xml.gz");
-        Resources.npcs        = (NpcDef[])        ResourceLoader.loadGzipData("NPCs.xml.gz");
-        Resources.objects     = (GameObjectDef[]) ResourceLoader.loadGzipData("Objects.xml.gz");
-        Resources.prayers     = (PrayerDef[])     ResourceLoader.loadGzipData("Prayers.xml.gz");
-        Resources.spells      = (SpellDef[])      ResourceLoader.loadGzipData("Spells.xml.gz");
-        Resources.textureDefs = (TextureDef[])    ResourceLoader.loadGzipData("Textures.xml.gz");
-        Resources.tiles       = (TileDef[])       ResourceLoader.loadGzipData("Tiles.xml.gz");
+        Resources.animations = (AnimationDef[]) ResourceLoader.loadGzipData("Animations.xml.gz");
+        Resources.doors = (DoorDef[]) ResourceLoader.loadGzipData("Doors.xml.gz");
+        Resources.elevation = (ElevationDef[]) ResourceLoader.loadGzipData("Elevation.xml.gz");
+        Resources.items = (ItemDef[]) ResourceLoader.loadGzipData("Items.xml.gz");
+        Resources.npcs = (NpcDef[]) ResourceLoader.loadGzipData("NPCs.xml.gz");
+        Resources.objects = (GameObjectDef[]) ResourceLoader.loadGzipData("Objects.xml.gz");
+        Resources.prayers = (PrayerDef[]) ResourceLoader.loadGzipData("Prayers.xml.gz");
+        Resources.spells = (SpellDef[]) ResourceLoader.loadGzipData("Spells.xml.gz");
+        Resources.textureDefs = (TextureDef[]) ResourceLoader.loadGzipData("Textures.xml.gz");
+        Resources.tiles = (TileDef[]) ResourceLoader.loadGzipData("Tiles.xml.gz");
 
         // Initialise items
         for (int id = 0; id < Resources.items.length; id++) {
@@ -144,8 +145,7 @@ private void loadGameData() {
 
         // Initialise objects
         for (int id = 0; id < Resources.objects.length; id++) {
-            Resources.objects[id].modelID =
-                    getModelIndex(Resources.objects[id].getObjectModel());
+            Resources.objects[id].modelID = getModelIndex(Resources.objects[id].getObjectModel());
         }
     }
 
@@ -205,8 +205,8 @@ public boolean loadSprite(int id, String packageName) {
                 System.err.println("Missing sprite: " + id);
                 return false;
             }
-            ByteBuffer data = DataUtils.streamToBuffer(new BufferedInputStream(
-                    Resources.spriteArchive.getInputStream(e)));
+            ByteBuffer data = DataUtils
+                    .streamToBuffer(new BufferedInputStream(Resources.spriteArchive.getInputStream(e)));
             Resources.sprites[id] = Sprite.deserialise(data);
             return true;
         } catch (Exception e) {
@@ -285,6 +285,10 @@ protected final void updateProgress(int progress, String message) {
         this.message = message;
     }
 
+    @Override
+    public void reset() {
+    }
+
     public boolean isLoaded() {
         return progress >= 100;
     }
diff --git a/rsc-client/src/client/loading/LoadingScreenRenderer.java b/rsc-client/src/client/loading/LoadingScreenRenderer.java
index eb0688b..179e8c6 100644
--- a/rsc-client/src/client/loading/LoadingScreenRenderer.java
+++ b/rsc-client/src/client/loading/LoadingScreenRenderer.java
@@ -42,25 +42,18 @@ public void render(Canvas canvas) {
         int y = canvas.getHeight() / 2 - BAR_HEIGHT / 2;
         g.setColor(BAR_COLOUR);
         g.drawRect(x - 2, y - 2, OUTLINE_WIDTH, OUTLINE_HEIGHT);
-        g.fillRect(x, y,
-                (BAR_WIDTH * loadingScreen.getProgress()) / 100,
-                BAR_HEIGHT);
+        g.fillRect(x, y, (BAR_WIDTH * loadingScreen.getProgress()) / 100, BAR_HEIGHT);
 
         // Draw loading message
         g.setColor(Color.WHITE);
-        drawString(g, loadingScreen.getMessage(), LOADING_FONT,
-                x + BAR_WIDTH / 2,
-                y + BAR_HEIGHT / 2);
+        drawString(g, loadingScreen.getMessage(), LOADING_FONT, x + BAR_WIDTH / 2, y + BAR_HEIGHT / 2);
     }
 
-    private static void drawString(Graphics g, String s,
-            Font font, int x, int y) {
+    private static void drawString(Graphics g, String s, Font font, int x, int y) {
         FontMetrics fontMetrics = g.getFontMetrics(font);
         fontMetrics.stringWidth(s);
         g.setFont(font);
-        g.drawString(s,
-                x - fontMetrics.stringWidth(s) / 2,
-                y + fontMetrics.getHeight() / 4);
+        g.drawString(s, x - fontMetrics.stringWidth(s) / 2, y + fontMetrics.getHeight() / 4);
     }
 
 }
diff --git a/rsc-client/src/client/login/LoginScreen.java b/rsc-client/src/client/login/LoginScreen.java
index cceb5c8..7e9189e 100644
--- a/rsc-client/src/client/login/LoginScreen.java
+++ b/rsc-client/src/client/login/LoginScreen.java
@@ -1,19 +1,14 @@
 package client.login;
 
-import java.io.IOException;
-import java.net.Socket;
+import java.util.Random;
 
 import client.RuneClient;
 import client.State;
 import client.StateRenderer;
-import client.game.Game;
-import client.net.Connection;
+import client.net.Packet;
 
 public class LoginScreen extends State {
 
-    private static final String SERVER_ADDRESS = "localhost";
-    private static final int SERVER_PORT = 7780;
-
     private LoginScreenRenderer renderer;
 
     public LoginScreen(RuneClient launcher) {
@@ -31,24 +26,41 @@ public StateRenderer getRenderer() {
     public void pollInput() {
         if (input.wasLeftClickReleased()) {
             // For now, just connect to the server immediately
-            connect(SERVER_ADDRESS, SERVER_PORT);
+
+            boolean connected = launcher.isConnected() || launcher.connect("localhost", 7780);
+            if (connected) {
+                sendLoginRequest("Player" + new Random().nextInt(1000), "topsecret");
+            }
         }
     }
 
-    private void connect(String address, int port) {
+    @Override
+    public void tick() {
+    }
 
-        Connection conn = null;
+    /**
+     * Sends a login request to the server.
+     */
+    private void sendLoginRequest(String name, String password) {
+        // Create a packet.
+        // Send the username and password.
+        Packet packet = new Packet(2);
 
-        try {
-            Socket socket = new Socket(address, port);
-            conn = new Connection(socket);
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
+        // Send the client build.
+        packet.putDouble(0.1); // TODO Add client build value to code somewhere?
 
-        if (conn != null) {
-            launcher.changeState(new Game(launcher, conn));
-        }
+        // Send name
+        packet.putString(name);
+
+        // Send password
+        packet.putString(password); // TODO Use RSA
+
+        // Send the packet.
+        launcher.sendPacket(packet);
+    }
+
+    @Override
+    public void reset() {
     }
 
 }
diff --git a/rsc-client/src/client/login/LoginScreenRenderer.java b/rsc-client/src/client/login/LoginScreenRenderer.java
index 3851c87..8c231de 100644
--- a/rsc-client/src/client/login/LoginScreenRenderer.java
+++ b/rsc-client/src/client/login/LoginScreenRenderer.java
@@ -26,13 +26,12 @@ public class LoginScreenRenderer extends StateRenderer {
      */
     public static final int LOGO_SPRITE_WIDTH = 438;
 
-    public LoginScreenRenderer(LoginScreen loginScreen) {}
+    public LoginScreenRenderer(LoginScreen loginScreen) {
+    }
 
     @Override
     public void render(Canvas canvas) {
-        int x = canvas.getWidth() / 2
-                - LOGO_SPRITE_WIDTH / 2
-                - LOGO_SPRITE_OFFSET_X;
+        int x = canvas.getWidth() / 2 - LOGO_SPRITE_WIDTH / 2 - LOGO_SPRITE_OFFSET_X;
         canvas.drawSprite(x, 50, SPRITE_ID_LOGO);
     }
 
diff --git a/rsc-client/src/client/net/Connection.java b/rsc-client/src/client/net/Connection.java
index 524a2fe..c2cb148 100644
--- a/rsc-client/src/client/net/Connection.java
+++ b/rsc-client/src/client/net/Connection.java
@@ -1,66 +1,104 @@
-package client.net;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.net.Socket;
-import java.util.ArrayList;
-import java.util.List;
-
-import client.packets.Packet;
-
-public class Connection implements PacketListener {
-
-    private Socket socket;
-    private DataInputStream in;
-    private DataOutputStream out;
-    private PacketReaderThread packetReaderThread;
-    private List packetsReceived = new ArrayList<>();
-    private boolean closed;
-
-    public Connection(Socket socket)
-            throws IOException {
-
-        this.socket = socket;
-
-        in = new DataInputStream(socket.getInputStream());
-        out = new DataOutputStream(socket.getOutputStream());
-
-        packetReaderThread = new PacketReaderThread(in, this);
-    }
-
-    public Runnable getPacketReaderThread() {
-        return packetReaderThread;
-    }
-
-    @Override
-    public void packetReceived(Packet p) {
-        synchronized (packetsReceived) {
-            packetsReceived.add(p);
-        }
-    }
-
-    @Override
-    public void packetReadError(IOException e) {
-        packetReaderThread.stop();
-        close();
-    }
-
-    public List getPacketsReceived() {
-        synchronized (packetsReceived) {
-            List packets = new ArrayList(packetsReceived);
-            packetsReceived.clear();
-            return packets;
-        }
-    }
-
-    public void close() {
-        closed = true;
-        SocketUtils.close(socket);
-    }
-
-    public boolean isClosed() {
-        return closed;
-    }
-
-}
+package client.net;
+
+import static org.jboss.netty.channel.Channels.pipeline;
+
+import java.net.ConnectException;
+import java.net.InetSocketAddress;
+import java.util.concurrent.Executors;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.jboss.netty.bootstrap.ClientBootstrap;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.channel.ChannelPipelineFactory;
+import org.jboss.netty.channel.ChannelStateEvent;
+import org.jboss.netty.channel.ExceptionEvent;
+import org.jboss.netty.channel.MessageEvent;
+import org.jboss.netty.channel.SimpleChannelHandler;
+import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
+
+import client.RuneClient;
+
+public class Connection extends SimpleChannelHandler implements ChannelPipelineFactory {
+
+    private Logger logger = Logger.getLogger(getClass().getName());
+
+    private final RuneClient client;
+    private Channel channel;
+
+    public Connection(final RuneClient client) {
+        this.client = client;
+    }
+
+    @Override
+    public ChannelPipeline getPipeline() throws Exception {
+        ChannelPipeline pipeline = pipeline();
+        pipeline.addLast("decoder", new PacketDecoder());
+        pipeline.addLast("encoder", new PacketEncoder());
+        pipeline.addLast("handler", this);
+        return pipeline;
+    }
+
+    @Override
+    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
+        Packet packet = (Packet) e.getMessage();
+        if (packet == null) {
+            return;
+        }
+        client.queuePacket(packet);
+    }
+
+    @Override
+    public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
+    }
+
+    @Override
+    public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
+        if (e.getCause() instanceof ConnectException) {
+            logger.log(Level.INFO, "Connection refused.", e);
+            return;
+        }
+        logger.log(Level.WARNING, "Exception caught in network.", e.getCause());
+    }
+
+    public boolean connect(String hostname, int port) {
+        try {
+            ClientBootstrap bootstrap = new ClientBootstrap(new NioClientSocketChannelFactory(
+                    Executors.newSingleThreadExecutor(), Executors.newCachedThreadPool(), 2));
+            bootstrap.setPipelineFactory(this);
+            channel = bootstrap.connect(new InetSocketAddress(hostname, port)).awaitUninterruptibly().getChannel();
+        } catch (Exception e) {
+            logger.log(Level.WARNING, "Error connecting to server.", e);
+            return false;
+        }
+        return isConnected();
+    }
+
+    public void disconnect() {
+        if (channel != null) {
+            channel.close();
+        }
+    }
+
+    public boolean isConnected() {
+        return channel != null ? channel.isConnected() : false;
+    }
+
+    /**
+     * Sends a packet to the server.
+     */
+    public void sendPacket(Packet packet) {
+        if (!channel.isConnected()) {
+            logger.log(Level.WARNING, "Error sending packet #" + packet.getOpcode() + ". Not connected.");
+            return;
+        }
+        channel.write(packet);
+    }
+
+}
\ No newline at end of file
diff --git a/rsc-client/src/client/net/Packet.java b/rsc-client/src/client/net/Packet.java
new file mode 100644
index 0000000..1314746
--- /dev/null
+++ b/rsc-client/src/client/net/Packet.java
@@ -0,0 +1,292 @@
+package client.net;
+
+import java.util.Arrays;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+
+/**
+ * A dynamic object that contains data sent from/to client/server.
+ */
+public class Packet {
+
+    private ChannelBuffer buffer = null;
+    private int opcode = 0;
+
+    /**
+     * Creating a new empty packet.
+     * 
+     * @param opcode
+     *            the opcode of the packet
+     */
+    public Packet(int opcode) {
+        // Creates a new big-endian dynamic buffer with the specified estimated data
+        // length.
+        // More accurate estimation yields less unexpected reallocation overhead.
+        this.buffer = ChannelBuffers.dynamicBuffer(4096/* 8192 */);
+        this.opcode = opcode;
+    }
+
+    /**
+     * Creating an already filled packet, used at the handling of the incoming
+     * packets
+     *
+     * @param opcode
+     *            the opcode of the packet
+     * @param buffer
+     *            the buffer
+     * @param length
+     *            the length of the packet
+     */
+    public Packet(int opcode, ChannelBuffer buffer, int length) {
+        this.buffer = ChannelBuffers.copiedBuffer(buffer.readBytes(length));
+        this.opcode = opcode;
+    }
+
+    public int getOpcode() {
+        return opcode;
+    }
+
+    /**
+     * The byte data type is an 8-bit signed two's complement integer. It has a
+     * minimum value of -128 and a maximum value of 127 (inclusive).
+     */
+    public Packet putByte(int b) {
+        buffer.writeByte(b);
+        return this;
+    }
+
+    public Packet putBytes(byte[] b) {
+        buffer.writeLong(b.length);
+        buffer.writeBytes(b);
+        return this;
+    }
+
+    /**
+     * The short data type is a 16-bit signed two's complement integer. It has a
+     * minimum value of -32,768 and a maximum value of 32,767 (inclusive).
+     */
+    public Packet putShort(short s) {
+        buffer.writeShort(s);
+        return this;
+    }
+
+    /**
+     * Sends a small integer (a short).
+     */
+    public Packet putSmallInt(int i) {
+        buffer.writeShort(i);
+        return this;
+    }
+
+    /**
+     * The int data type is a 32-bit signed two's complement integer. It has a
+     * minimum value of -2,147,483,648 and a maximum value of 2,147,483,647
+     * (inclusive).
+     */
+    public Packet putInt(int i) {
+        buffer.writeInt(i);
+        return this;
+    }
+
+    /**
+     * long: The long data type is a 64-bit signed two's complement integer. It has
+     * a minimum value of -9,223,372,036,854,775,808 and a maximum value of
+     * 9,223,372,036,854,775,807 (inclusive). Use this data type when you need a
+     * range of values wider than those provided by int.
+     */
+    public Packet putLong(long l) {
+        buffer.writeLong(l);
+        return this;
+    }
+
+    /**
+     * Encodes a String into a base37 integral value.
+     * 
+     * @return The encoded String value.
+     */
+    public Packet putBase37(String string) {
+        String s = "";
+        for (int i = 0; i < string.length(); i++) {
+            char c = string.charAt(i);
+            if (c >= 'a' && c <= 'z') {
+                s = s + c;
+            } else if (c >= 'A' && c <= 'Z') {
+                s = s + (char) ((c + 97) - 65);
+            } else if (c >= '0' && c <= '9') {
+                s = s + c;
+            } else {
+                s = s + ' ';
+            }
+        }
+
+        if (s.length() > 12) {
+            s = s.substring(0, 12);
+        }
+        long base = 0L;
+        for (int i = 0; i < s.length(); i++) {
+            char c1 = s.charAt(i);
+            base *= 37L;
+            if (c1 >= 'a' && c1 <= 'z') {
+                base += (1 + c1) - 97;
+            } else if (c1 >= '0' && c1 <= '9') {
+                base += (27 + c1) - 48;
+            }
+        }
+        buffer.writeLong(base);
+        return this;
+    }
+
+    /**
+     * https://docs.oracle.com/javase/7/docs/api/java/lang/Double.html
+     */
+    public Packet putDouble(double d) {
+        buffer.writeDouble(d);
+        return this;
+    }
+
+    /**
+     * https://docs.oracle.com/javase/7/docs/api/java/lang/Float.html
+     */
+    public Packet putFloat(float f) {
+        return putString(Float.toString(f));
+    }
+
+    public Packet putBoolean(boolean b) {
+        buffer.writeByte((byte) (b ? 1 : 0));
+        return this;
+    }
+
+    public Packet putString(String s) {
+        buffer.writeByte(s.getBytes().length);
+        buffer.writeBytes(s.getBytes());
+        return this;
+    }
+
+    /**
+     * @return Reads a Byte value from the buffer.
+     */
+    public byte getByte() {
+        return buffer.readByte();
+    }
+
+    /**
+     * @return Reads an array of Byte values from the buffer.
+     */
+    public byte[] getBytes() {
+        int length = (int) buffer.readLong();
+        byte[] b = new byte[length];
+        for (int i = 0; i < length; i++) {
+            b[i] = buffer.readByte();
+        }
+        return b;
+    }
+
+    /**
+     * @return Reads a Short from the buffer.
+     */
+    public short getShort() {
+        return buffer.readShort();
+    }
+
+    /**
+     * @return Reads an Small Integer (Short) from the buffer.
+     */
+    public int getSmallInt() {
+        return buffer.readShort();
+    }
+
+    /**
+     * @return Reads an Integer from the buffer.
+     */
+    public int getInt() {
+        return buffer.readInt();
+    }
+
+    /**
+     * @return Reads a Long from the buffer.
+     */
+    public long getLong() {
+        return buffer.readLong();
+    }
+
+    /**
+     * Decodes a base37 integral value into a String.
+     * 
+     * @return The decoded String value.
+     */
+    public String getBase37() {
+        long base = buffer.readLong();
+        if (base < 0L) {
+            return "invalid_name";
+        }
+        String string = "";
+        while (base != 0L) {
+            int value = (int) (base % 37L);
+            base /= 37L;
+            if (value == 0) {
+                string = " " + string;
+            } else if (value < 27) {
+                if (base % 37L == 0L) {
+                    string = (char) ((value + 65) - 1) + string;
+                } else {
+                    string = (char) ((value + 97) - 1) + string;
+                }
+            } else {
+                string = (char) ((value + 48) - 27) + string;
+            }
+        }
+        return string;
+    }
+
+    /**
+     * @return Reads a Double from the buffer.
+     */
+    public double getDouble() {
+        return buffer.readDouble();
+    }
+
+    /**
+     * @return Reads a Float from the buffer.
+     */
+    public float getFloat() {
+        return Float.parseFloat(getString());
+    }
+
+    /**
+     * @return Reads a Boolean from the buffer.
+     */
+    public boolean getBoolean() {
+        return buffer.readByte() == 1;
+    }
+
+    /**
+     * @return Reads a String from the buffer.
+     */
+    public String getString() {
+        int length = getByte();
+        byte[] b = new byte[length];
+        for (int i = 0; i < b.length; i++) {
+            b[i] = buffer.readByte();
+        }
+        return new String(b);
+    }
+
+    /**
+     * @return The byte array contains all of the packet data.
+     */
+    public byte[] toByteArray() {
+        if (buffer.hasArray()) {
+            return Arrays.copyOfRange(buffer.array(), 0, buffer.writerIndex());
+        }
+        throw new IllegalStateException();
+    }
+
+    /**
+     * @return The packet length.
+     */
+    public int getPacketLength() {
+        return buffer.readableBytes();
+    }
+
+}
\ No newline at end of file
diff --git a/rsc-client/src/client/net/PacketDecoder.java b/rsc-client/src/client/net/PacketDecoder.java
new file mode 100644
index 0000000..83f38f7
--- /dev/null
+++ b/rsc-client/src/client/net/PacketDecoder.java
@@ -0,0 +1,51 @@
+package client.net;
+
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.handler.codec.frame.FrameDecoder;
+
+/**
+ * Reads an incoming packet.
+ */
+public class PacketDecoder extends FrameDecoder {
+
+    @Override
+    protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
+        // Make sure the length field was received.
+        if (buffer.readableBytes() < 4) {
+            // The length field was not received yet - return null.
+            // This method will be invoked again when more packets are
+            // received and appended to the buffer.
+            return null;
+        }
+
+        // Mark the current buffer position before reading the length field
+        // because the whole frame might not be in the buffer yet.
+        // We will reset the buffer position to the marked position if
+        // there's not enough bytes in the buffer.
+        buffer.markReaderIndex();
+
+        // Read the length field.
+        final int length = buffer.readInt();
+
+        // Make sure if there's enough bytes in the buffer.
+        if (buffer.readableBytes() < length + 4) {
+            // The whole bytes were not received yet - return null.
+            // This method will be invoked again when more packets are
+            // received and appended to the buffer.
+
+            // Reset to the marked position to read the length field again
+            // next time.
+            buffer.resetReaderIndex();
+            return null;
+        }
+
+        // Read the opcode.
+        final int opcode = buffer.readInt() & 0xff;
+
+        // Create an executable packet using the buffer data.
+        return new Packet(opcode, buffer, length);
+    }
+
+}
\ No newline at end of file
diff --git a/rsc-client/src/client/net/PacketEncoder.java b/rsc-client/src/client/net/PacketEncoder.java
new file mode 100644
index 0000000..30e1712
--- /dev/null
+++ b/rsc-client/src/client/net/PacketEncoder.java
@@ -0,0 +1,35 @@
+package client.net;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
+
+/**
+ * Writes an outgoing packet.
+ */
+public class PacketEncoder extends OneToOneEncoder {
+
+    @Override
+    protected Object encode(ChannelHandlerContext ctx, Channel channel, Object obj) throws Exception {
+        // Get the packet object.
+        Packet packet = (Packet) obj;
+
+        // Create a new buffer.
+        ChannelBuffer buffer = ChannelBuffers.dynamicBuffer();
+
+        // Write the packet length.
+        buffer.writeInt(packet.getPacketLength());
+
+        // Write the packet opcode.
+        buffer.writeInt(packet.getOpcode() & 0xff);
+
+        // Write the packet data
+        buffer.writeBytes(packet.toByteArray());
+
+        // The buffer.
+        return buffer;
+    }
+
+}
\ No newline at end of file
diff --git a/rsc-client/src/client/net/PacketListener.java b/rsc-client/src/client/net/PacketListener.java
deleted file mode 100644
index 36c112b..0000000
--- a/rsc-client/src/client/net/PacketListener.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package client.net;
-
-import java.io.IOException;
-
-import client.packets.Packet;
-
-public interface PacketListener {
-
-    void packetReceived(Packet p);
-
-    void packetReadError(IOException e);
-
-}
diff --git a/rsc-client/src/client/net/PacketReaderThread.java b/rsc-client/src/client/net/PacketReaderThread.java
deleted file mode 100644
index 95653bd..0000000
--- a/rsc-client/src/client/net/PacketReaderThread.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package client.net;
-
-import java.io.DataInputStream;
-import java.io.IOException;
-
-import client.packets.Packet;
-import client.packets.PacketDeserializer;
-import client.packets.PacketDeserializers;
-
-public class PacketReaderThread implements Runnable {
-
-    private DataInputStream in;
-
-    private PacketListener packetListener;
-
-    private boolean stopped;
-
-    public PacketReaderThread(
-            DataInputStream in, PacketListener packetListener) {
-
-        this.in = in;
-        this.packetListener = packetListener;
-    }
-
-    @Override
-    public void run() {
-        while (!stopped) {
-            try {
-
-                short id = in.readShort();
-                PacketDeserializer deserializer = PacketDeserializers.get(id);
-
-                if (deserializer != null) {
-                    Packet p = deserializer.deserialize(in);
-                    packetListener.packetReceived(p);
-                }
-
-            } catch (IOException e) {
-                e.printStackTrace();
-                packetListener.packetReadError(e);
-            }
-        }
-    }
-
-    public void stop() {
-        stopped = true;
-    }
-
-}
diff --git a/rsc-client/src/client/net/SocketUtils.java b/rsc-client/src/client/net/SocketUtils.java
deleted file mode 100644
index f5ab485..0000000
--- a/rsc-client/src/client/net/SocketUtils.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package client.net;
-
-import java.io.IOException;
-import java.net.Socket;
-
-public class SocketUtils {
-
-    /**
-     * Closes a Socket safely.
-     *
-     * @param socket
-     */
-    public static void close(Socket socket) {
-        if (socket != null && !socket.isClosed()) {
-            try {
-                socket.close();
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-        }
-    }
-
-}
diff --git a/rsc-client/src/client/packets/Packet.java b/rsc-client/src/client/packets/Packet.java
deleted file mode 100644
index 435fd07..0000000
--- a/rsc-client/src/client/packets/Packet.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package client.packets;
-
-public abstract class Packet {
-
-    // Server Packet IDs
-    public static final short LOGIN_SUCCESS = 0;
-    public static final short KICK = 1;
-
-    public final short id;
-
-    public Packet(short id) {
-        this.id = id;
-    }
-
-}
diff --git a/rsc-client/src/client/packets/PacketDeserializer.java b/rsc-client/src/client/packets/PacketDeserializer.java
deleted file mode 100644
index ff58fc7..0000000
--- a/rsc-client/src/client/packets/PacketDeserializer.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package client.packets;
-
-import java.io.DataInputStream;
-import java.io.IOException;
-
-public abstract class PacketDeserializer {
-
-    public abstract Packet deserialize(DataInputStream in) throws IOException;
-
-}
diff --git a/rsc-client/src/client/packets/PacketDeserializers.java b/rsc-client/src/client/packets/PacketDeserializers.java
deleted file mode 100644
index 9859afd..0000000
--- a/rsc-client/src/client/packets/PacketDeserializers.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package client.packets;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import client.packets.deserializers.LoginSuccessPacketDeserializer;
-
-public class PacketDeserializers {
-
-    private static Map registeredDeserializers =
-            new HashMap<>();
-
-    static {
-        register(Packet.LOGIN_SUCCESS, new LoginSuccessPacketDeserializer());
-    }
-
-    private static void register(short id, PacketDeserializer serializer) {
-        registeredDeserializers.put(id, serializer);
-    }
-
-    public static PacketDeserializer get(short id) {
-        return registeredDeserializers.get(id);
-    }
-
-}
diff --git a/rsc-client/src/client/packets/PacketHandler.java b/rsc-client/src/client/packets/PacketHandler.java
deleted file mode 100644
index 6403fa5..0000000
--- a/rsc-client/src/client/packets/PacketHandler.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package client.packets;
-
-import client.game.Game;
-
-public abstract class PacketHandler {
-
-    public abstract void apply(Packet packetBeforeCast, Game game);
-
-}
diff --git a/rsc-client/src/client/packets/PacketHandlers.java b/rsc-client/src/client/packets/PacketHandlers.java
deleted file mode 100644
index 5ac8e62..0000000
--- a/rsc-client/src/client/packets/PacketHandlers.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package client.packets;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import client.packets.handlers.LoginSuccessPacketHandler;
-
-public class PacketHandlers {
-
-    private static Map registeredHandlers =
-            new HashMap<>();
-
-    static {
-        register(Packet.LOGIN_SUCCESS, new LoginSuccessPacketHandler());
-    }
-
-    private static void register(short id, PacketHandler serializer) {
-        registeredHandlers.put(id, serializer);
-    }
-
-    public static PacketHandler get(short id) {
-        return registeredHandlers.get(id);
-    }
-
-}
diff --git a/rsc-client/src/client/packets/PacketSerializer.java b/rsc-client/src/client/packets/PacketSerializer.java
deleted file mode 100644
index d032184..0000000
--- a/rsc-client/src/client/packets/PacketSerializer.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package client.packets;
-
-import java.io.DataOutputStream;
-import java.io.IOException;
-
-public abstract class PacketSerializer {
-
-    public abstract void serialize(
-            DataOutputStream out, Packet packetBeforeCast) throws IOException;
-
-}
diff --git a/rsc-client/src/client/packets/PacketSerializers.java b/rsc-client/src/client/packets/PacketSerializers.java
deleted file mode 100644
index e2dbb5b..0000000
--- a/rsc-client/src/client/packets/PacketSerializers.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package client.packets;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class PacketSerializers {
-
-    private static Map registeredSerializers =
-            new HashMap<>();
-
-    static {
-        // TODO: register serializers
-    }
-
-    private static void register(short id, PacketSerializer serializer) {
-        registeredSerializers.put(id, serializer);
-    }
-
-    public static PacketSerializer get(short id) {
-        return registeredSerializers.get(id);
-    }
-
-}
diff --git a/rsc-client/src/client/packets/builders/PacketBuilder.java b/rsc-client/src/client/packets/builders/PacketBuilder.java
deleted file mode 100644
index 965ad69..0000000
--- a/rsc-client/src/client/packets/builders/PacketBuilder.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package client.packets.builders;
-
-import client.packets.Packet;
-
-public abstract class PacketBuilder {
-
-    public abstract Packet build();
-
-}
diff --git a/rsc-client/src/client/packets/deserializers/LoginSuccessPacketDeserializer.java b/rsc-client/src/client/packets/deserializers/LoginSuccessPacketDeserializer.java
deleted file mode 100644
index f357dc3..0000000
--- a/rsc-client/src/client/packets/deserializers/LoginSuccessPacketDeserializer.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package client.packets.deserializers;
-
-import java.io.DataInputStream;
-import java.io.IOException;
-
-import client.packets.Packet;
-import client.packets.PacketDeserializer;
-import client.packets.from_server.LoginSuccessPacket;
-
-public class LoginSuccessPacketDeserializer extends PacketDeserializer {
-
-    @Override
-    public Packet deserialize(DataInputStream in) throws IOException {
-        return new LoginSuccessPacket();
-    }
-
-}
diff --git a/rsc-client/src/client/packets/from_server/KickPacket.java b/rsc-client/src/client/packets/from_server/KickPacket.java
deleted file mode 100644
index e605d58..0000000
--- a/rsc-client/src/client/packets/from_server/KickPacket.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package client.packets.from_server;
-
-import client.packets.Packet;
-
-public class KickPacket extends Packet {
-
-    public final byte reason;
-
-    public KickPacket(byte reason) {
-        super(Packet.KICK);
-
-        this.reason = reason;
-    }
-
-}
diff --git a/rsc-client/src/client/packets/from_server/LoginSuccessPacket.java b/rsc-client/src/client/packets/from_server/LoginSuccessPacket.java
deleted file mode 100644
index db997dc..0000000
--- a/rsc-client/src/client/packets/from_server/LoginSuccessPacket.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package client.packets.from_server;
-
-import client.packets.Packet;
-
-public class LoginSuccessPacket extends Packet {
-
-    public LoginSuccessPacket() {
-        super(Packet.LOGIN_SUCCESS);
-    }
-
-}
diff --git a/rsc-client/src/client/packets/handlers/LoginSuccessPacketHandler.java b/rsc-client/src/client/packets/handlers/LoginSuccessPacketHandler.java
deleted file mode 100644
index 6c21eaf..0000000
--- a/rsc-client/src/client/packets/handlers/LoginSuccessPacketHandler.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package client.packets.handlers;
-
-import client.game.Game;
-import client.packets.Packet;
-import client.packets.PacketHandler;
-import client.packets.from_server.LoginSuccessPacket;
-
-public class LoginSuccessPacketHandler extends PacketHandler {
-
-    @Override
-    public void apply(Packet packetBeforeCast, Game game) {
-        LoginSuccessPacket p = (LoginSuccessPacket) packetBeforeCast;
-        game.loggedIn();
-    }
-
-}
diff --git a/rsc-client/src/client/res/Fonts.java b/rsc-client/src/client/res/Fonts.java
index fc11476..6c6d419 100644
--- a/rsc-client/src/client/res/Fonts.java
+++ b/rsc-client/src/client/res/Fonts.java
@@ -11,8 +11,7 @@
 
 public abstract class Fonts {
 
-    private static final String ALLOWED_CHARS =
-            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!\"£$%^&*()-_=+[{]};:'@#~,<.>/?\\| ";
+    private static final String ALLOWED_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!\"£$%^&*()-_=+[{]};:'@#~,<.>/?\\| ";
     private static final String CHARS_WITH_EXTRA_WIDTH = "ftwvkxyAVW";
     private static final int NUM_CHAR_PROPERTIES = 9;
 
@@ -32,6 +31,7 @@ public abstract class Fonts {
             charIndexes[i] = index * NUM_CHAR_PROPERTIES;
         }
     }
+
     public static void loadFonts(JFrame frame) {
         loadFont("helvetica", 11, Font.PLAIN, 0, frame, false, false);
         loadFont("helvetica", 12, Font.BOLD, 1, frame, false, false);
@@ -43,17 +43,16 @@ public static void loadFonts(JFrame frame) {
         loadFont("helvetica", 24, Font.BOLD, 7, frame, false, false);
     }
 
-    private static void loadFont(String fontName, int size, int style,
-            int fontNumber, JFrame frame, boolean fontWasRedrawn,
-            boolean addCharWidth) {
+    private static void loadFont(String fontName, int size, int style, int fontNumber, JFrame frame,
+            boolean fontWasRedrawn, boolean addCharWidth) {
 
         Font font = new Font("Helvetica", style, size);
         FontMetrics fontmetrics = frame.getFontMetrics(font);
 
         nextDataIndex = ALLOWED_CHARS.length() * NUM_CHAR_PROPERTIES;
         for (int i = 0; i < ALLOWED_CHARS.length(); i++) {
-            drawLetter(font, fontmetrics, ALLOWED_CHARS.charAt(i), i, frame,
-                    fontNumber * NUM_CHAR_PROPERTIES, addCharWidth);
+            drawLetter(font, fontmetrics, ALLOWED_CHARS.charAt(i), i, frame, fontNumber * NUM_CHAR_PROPERTIES,
+                    addCharWidth);
         }
 
         // First 855 elements of fontProperties for a given font are the 9
@@ -74,8 +73,8 @@ private static void loadFont(String fontName, int size, int style,
         }
     }
 
-    public static void drawLetter(Font font, FontMetrics fontMetrics, char c,
-            int charIndex, JFrame frame, int fontNumber, boolean addCharWidth) {
+    public static void drawLetter(Font font, FontMetrics fontMetrics, char c, int charIndex, JFrame frame,
+            int fontNumber, boolean addCharWidth) {
 
         // Determine properties of this character
         int charWidth = fontMetrics.charWidth(c);
@@ -84,13 +83,12 @@ public static void drawLetter(Font font, FontMetrics fontMetrics, char c,
             if (c == '/') {
                 addCharWidth = false;
             }
-            if (CHARS_WITH_EXTRA_WIDTH.indexOf(c) > 0){
+            if (CHARS_WITH_EXTRA_WIDTH.indexOf(c) > 0) {
                 charWidth++;
             }
         }
         int maxAscent = fontMetrics.getMaxAscent();
-        int maxCharHeight = 
-                fontMetrics.getMaxAscent() + fontMetrics.getMaxDescent();
+        int maxCharHeight = fontMetrics.getMaxAscent() + fontMetrics.getMaxDescent();
         int standardCharHeight = fontMetrics.getHeight();
 
         // Create a blank image
@@ -109,8 +107,7 @@ public static void drawLetter(Font font, FontMetrics fontMetrics, char c,
 
         // Get pixels from image
         int pix[] = new int[charWidth * maxCharHeight];
-        PixelGrabber pixelgrabber = new PixelGrabber(image, 0, 0, charWidth,
-                maxCharHeight, pix, 0, charWidth);
+        PixelGrabber pixelgrabber = new PixelGrabber(image, 0, 0, charWidth, maxCharHeight, pix, 0, charWidth);
         try {
             pixelgrabber.grabPixels();
         } catch (InterruptedException ex) {
@@ -124,86 +121,82 @@ public static void drawLetter(Font font, FontMetrics fontMetrics, char c,
         int drawWidth = charWidth;
         int drawHeight = maxCharHeight;
 
-        searchRowsDown:
-            for (int y = 0; y < maxCharHeight; y++) {
-                for (int x = 0; x < charWidth; x++) {
-                    int col = pix[x + y * charWidth];
-                    if ((col & 0xffffff) == 0) {
-                        // Pixel has no colour
-                        continue;
-                    }
-                    drawOffsetY = y;
-                    break searchRowsDown;
+        searchRowsDown: for (int y = 0; y < maxCharHeight; y++) {
+            for (int x = 0; x < charWidth; x++) {
+                int col = pix[x + y * charWidth];
+                if ((col & 0xffffff) == 0) {
+                    // Pixel has no colour
+                    continue;
                 }
-
+                drawOffsetY = y;
+                break searchRowsDown;
             }
 
-        searchColumnsRight:
-            for (int x = 0; x < charWidth; x++) {
-                for (int y = 0; y < maxCharHeight; y++) {
-                    int col = pix[x + y * charWidth];
-                    if ((col & 0xffffff) == 0) {
-                        // Pixel has no colour
-                        continue;
-                    }
-                    drawOffsetX = x;
-                    break searchColumnsRight;
-                }
+        }
 
+        searchColumnsRight: for (int x = 0; x < charWidth; x++) {
+            for (int y = 0; y < maxCharHeight; y++) {
+                int col = pix[x + y * charWidth];
+                if ((col & 0xffffff) == 0) {
+                    // Pixel has no colour
+                    continue;
+                }
+                drawOffsetX = x;
+                break searchColumnsRight;
             }
 
-            searchRowsUp:
-                for (int y = maxCharHeight - 1; y >= 0; y--) {
-                    for (int x = 0; x < charWidth; x++) {
-                        int col = pix[x + y * charWidth];
-                        if ((col & 0xffffff) == 0) {
-                            // Pixel has no colour
-                            continue;
-                        }
-                        drawHeight = y + 1;
-                        break searchRowsUp;
-                    }
+        }
 
+        searchRowsUp: for (int y = maxCharHeight - 1; y >= 0; y--) {
+            for (int x = 0; x < charWidth; x++) {
+                int col = pix[x + y * charWidth];
+                if ((col & 0xffffff) == 0) {
+                    // Pixel has no colour
+                    continue;
                 }
-            searchColumnsLeft:
-                for (int x = charWidth - 1; x >= 0; x--) {
-                    for (int y = 0; y < maxCharHeight; y++) {
-                        int col = pix[x + y * charWidth];
-                        if ((col & 0xffffff) == 0) {
-                            // Pixel has no colour
-                            continue;
-                        }
-                        drawWidth = x + 1;
-                        break searchColumnsLeft;
-                    }
+                drawHeight = y + 1;
+                break searchRowsUp;
+            }
 
+        }
+        searchColumnsLeft: for (int x = charWidth - 1; x >= 0; x--) {
+            for (int y = 0; y < maxCharHeight; y++) {
+                int col = pix[x + y * charWidth];
+                if ((col & 0xffffff) == 0) {
+                    // Pixel has no colour
+                    continue;
                 }
+                drawWidth = x + 1;
+                break searchColumnsLeft;
+            }
 
-                // Store character properties
-                //     nextDataIndex: bits 11-18
-                fontData[charIndex]     = (byte) (nextDataIndex >> 14);
-                //     nextDataIndex: bits 19-26 (& excludes bit 18)
-                fontData[charIndex + 1] = (byte) ((nextDataIndex >> 7) & 127);
-                //     nextDataIndex: bits 27-32 (& excludes bit 26)
-                fontData[charIndex + 2] = (byte) (nextDataIndex & 127);
-                fontData[charIndex + 3] = (byte) (drawWidth - drawOffsetX);
-                fontData[charIndex + 4] = (byte) (drawHeight - drawOffsetY);
-                fontData[charIndex + 5] = (byte) drawOffsetX;
-                fontData[charIndex + 6] = (byte) (maxAscent - drawOffsetY);
-                fontData[charIndex + 7] = (byte) oldCharWidth;
-                fontData[charIndex + 8] = (byte) standardCharHeight;
-
-                // Store character pixel data
-                for (int x = drawOffsetY; x < drawHeight; x++) {
-                    for (int y = drawOffsetX; y < drawWidth; y++) {
-                        int col = pix[y + x * charWidth] & 0xff;
-                        if (col > 30 && col < 230) {
-                            redraw[fontNumber] = true;
-                        }
-                        fontData[nextDataIndex] = (byte) col;
-                        nextDataIndex++;
-                    }
+        }
+
+        // Store character properties
+        // nextDataIndex: bits 11-18
+        fontData[charIndex] = (byte) (nextDataIndex >> 14);
+        // nextDataIndex: bits 19-26 (& excludes bit 18)
+        fontData[charIndex + 1] = (byte) ((nextDataIndex >> 7) & 127);
+        // nextDataIndex: bits 27-32 (& excludes bit 26)
+        fontData[charIndex + 2] = (byte) (nextDataIndex & 127);
+        fontData[charIndex + 3] = (byte) (drawWidth - drawOffsetX);
+        fontData[charIndex + 4] = (byte) (drawHeight - drawOffsetY);
+        fontData[charIndex + 5] = (byte) drawOffsetX;
+        fontData[charIndex + 6] = (byte) (maxAscent - drawOffsetY);
+        fontData[charIndex + 7] = (byte) oldCharWidth;
+        fontData[charIndex + 8] = (byte) standardCharHeight;
+
+        // Store character pixel data
+        for (int x = drawOffsetY; x < drawHeight; x++) {
+            for (int y = drawOffsetX; y < drawWidth; y++) {
+                int col = pix[y + x * charWidth] & 0xff;
+                if (col > 30 && col < 230) {
+                    redraw[fontNumber] = true;
                 }
+                fontData[nextDataIndex] = (byte) col;
+                nextDataIndex++;
+            }
+        }
     }
 
 }
diff --git a/rsc-client/src/client/res/ResourceLoader.java b/rsc-client/src/client/res/ResourceLoader.java
index 7dca2bf..9d3f82e 100644
--- a/rsc-client/src/client/res/ResourceLoader.java
+++ b/rsc-client/src/client/res/ResourceLoader.java
@@ -22,8 +22,7 @@ public class ResourceLoader {
     /**
      * Package containing entity definitions.
      */
-    private static final String ENTITY_DEF_PACKAGE_NAME =
-            "client.entityhandling.defs";
+    private static final String ENTITY_DEF_PACKAGE_NAME = "client.entityhandling.defs";
 
     /**
      * XStream used to read from / write to XML.
@@ -32,22 +31,21 @@ public class ResourceLoader {
 
     static {
         // XStream aliases
-        addAlias("AnimationDef",  ENTITY_DEF_PACKAGE_NAME + ".AnimationDef");
-        addAlias("DoorDef",       ENTITY_DEF_PACKAGE_NAME + ".DoorDef");
-        addAlias("ElevationDef",  ENTITY_DEF_PACKAGE_NAME + ".ElevationDef");
+        addAlias("AnimationDef", ENTITY_DEF_PACKAGE_NAME + ".AnimationDef");
+        addAlias("DoorDef", ENTITY_DEF_PACKAGE_NAME + ".DoorDef");
+        addAlias("ElevationDef", ENTITY_DEF_PACKAGE_NAME + ".ElevationDef");
         addAlias("GameObjectDef", ENTITY_DEF_PACKAGE_NAME + ".GameObjectDef");
-        addAlias("ItemDef",       ENTITY_DEF_PACKAGE_NAME + ".ItemDef");
-        addAlias("ItemDropDef",   ENTITY_DEF_PACKAGE_NAME + ".ItemDropDef");
-        addAlias("NPCDef",        ENTITY_DEF_PACKAGE_NAME + ".NpcDef");
-        addAlias("PrayerDef",     ENTITY_DEF_PACKAGE_NAME + ".PrayerDef");
-        addAlias("SpellDef",      ENTITY_DEF_PACKAGE_NAME + ".SpellDef");
-        addAlias("TextureDef",    ENTITY_DEF_PACKAGE_NAME + ".TextureDef");
-        addAlias("TileDef",       ENTITY_DEF_PACKAGE_NAME + ".TileDef");
+        addAlias("ItemDef", ENTITY_DEF_PACKAGE_NAME + ".ItemDef");
+        addAlias("ItemDropDef", ENTITY_DEF_PACKAGE_NAME + ".ItemDropDef");
+        addAlias("NPCDef", ENTITY_DEF_PACKAGE_NAME + ".NpcDef");
+        addAlias("PrayerDef", ENTITY_DEF_PACKAGE_NAME + ".PrayerDef");
+        addAlias("SpellDef", ENTITY_DEF_PACKAGE_NAME + ".SpellDef");
+        addAlias("TextureDef", ENTITY_DEF_PACKAGE_NAME + ".TextureDef");
+        addAlias("TileDef", ENTITY_DEF_PACKAGE_NAME + ".TileDef");
     }
 
     public static InputStream getResourceAsStream(String filename) {
-        return ResourceLoader.class.getClassLoader()
-                .getResourceAsStream(filename);
+        return ResourceLoader.class.getClassLoader().getResourceAsStream(filename);
     }
 
     private static void addAlias(String name, String className) {
@@ -69,8 +67,7 @@ public static ZipFile loadZipData(String filename) {
 
     public static Object loadGzipData(String filename) {
         try {
-            InputStream is = new GZIPInputStream(
-                    getResourceAsStream(DATA_DIR + filename));
+            InputStream is = new GZIPInputStream(getResourceAsStream(DATA_DIR + filename));
             return xStream.fromXML(is);
         } catch (IOException ex) {
             ex.printStackTrace();
diff --git a/rsc-client/src/client/res/Resources.java b/rsc-client/src/client/res/Resources.java
index 2a054b0..9c0f3a7 100644
--- a/rsc-client/src/client/res/Resources.java
+++ b/rsc-client/src/client/res/Resources.java
@@ -21,7 +21,8 @@
 /**
  * Class responsible for holding the game's resources.
  *
- * 

Based on EntityHandler.java from other RSC sources. + *

+ * Based on EntityHandler.java from other RSC sources. * * @author Dan Bryce */ @@ -102,8 +103,7 @@ public static Sector loadSector(int sectionX, int sectionY, int layer) { } } } else { - ByteBuffer data = DataUtils.streamToBuffer( - new BufferedInputStream(tileArchive.getInputStream(e))); + ByteBuffer data = DataUtils.streamToBuffer(new BufferedInputStream(tileArchive.getInputStream(e))); s = Sector.unpack(data); } } catch (Exception e) { @@ -154,8 +154,8 @@ public static void prepareTexture(int id) { /* * Produce 3 additional versions of the texture. * - * These seem to be darker versions, which seem to be drawn over the - * normal texture during rendering. + * These seem to be darker versions, which seem to be drawn over the normal + * texture during rendering. */ for (int i = 0; i < pixelIndex; i++) { int colour = tex.pixels[i]; diff --git a/rsc-client/src/client/res/Texture.java b/rsc-client/src/client/res/Texture.java index 2cf084e..586594b 100644 --- a/rsc-client/src/client/res/Texture.java +++ b/rsc-client/src/client/res/Texture.java @@ -13,19 +13,19 @@ public class Texture { * Cleared after the Texture has been loaded. */ public byte colourData[]; - + /** * List of colours used by this Texture. * * Cleared after the Texture has been loaded. */ public int palette[]; - + /** * Pixel colours. */ public int pixels[]; - + /** * Whether or not this texture contains any transparent pixels. */ @@ -47,11 +47,11 @@ public Texture(byte[] colourData, int[] palette, boolean large) { public void setHasTransparency(boolean hasTransparency) { this.hasTransparency = hasTransparency; } - + public boolean hasTransparency() { return hasTransparency; } - + public boolean isLarge() { return large; } diff --git a/rsc-client/src/client/util/DataUtils.java b/rsc-client/src/client/util/DataUtils.java index b6fd9bd..aedb25b 100644 --- a/rsc-client/src/client/util/DataUtils.java +++ b/rsc-client/src/client/util/DataUtils.java @@ -16,8 +16,7 @@ public class DataUtils { * @return * @throws IOException */ - public static final ByteBuffer streamToBuffer(BufferedInputStream in) - throws IOException { + public static final ByteBuffer streamToBuffer(BufferedInputStream in) throws IOException { byte[] buffer = new byte[in.available()]; in.read(buffer, 0, buffer.length); return ByteBuffer.wrap(buffer); diff --git a/rsc-server/.classpath b/rsc-server/.classpath index 0c0bd07..ae8052e 100644 --- a/rsc-server/.classpath +++ b/rsc-server/.classpath @@ -1,9 +1,14 @@ - - - - - - - - - + + + + + + + + + + + + + + diff --git a/rsc-server/.gitignore b/rsc-server/.gitignore new file mode 100644 index 0000000..bde22fb --- /dev/null +++ b/rsc-server/.gitignore @@ -0,0 +1,2 @@ +/config.ini +/mob junk.txt diff --git a/rsc-server/libs/netty-3.10.6.Final.jar b/rsc-server/libs/netty-3.10.6.Final.jar new file mode 100644 index 0000000..b0a1bda Binary files /dev/null and b/rsc-server/libs/netty-3.10.6.Final.jar differ diff --git a/rsc-server/src/org/openrsc/Config.java b/rsc-server/src/org/openrsc/Config.java new file mode 100644 index 0000000..ec6a0e0 --- /dev/null +++ b/rsc-server/src/org/openrsc/Config.java @@ -0,0 +1,76 @@ +package org.openrsc; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +/** + * Engine configurations. + */ +public class Config { + + /** + * The listener port. + */ + public static final int SERVER_PORT = 7780; + + /** + * The maximum number of connections per address. + */ + public static final int CONNECTION_LIMIT = 3; + + /** + * The Executor which will execute the boss threads. + */ + public static final Executor NETTY_BOSS_EXECUTOR = Executors.newCachedThreadPool(); + + /** + * The Executor which will execute the I/O worker threads. + */ + public static final Executor NETTY_WORK_EXECUTOR = Executors.newCachedThreadPool(); + + /** + * The maximum number of I/O worker threads + */ + public static final int NETTY_MAXIMUM_WORKER_COUNT = Runtime.getRuntime().availableProcessors() * 2; + + /** + * The number of worker threads the task engine will use to process game + * tasks/events. + */ + public static final int TASK_ENGINE_THREAD_COUNT = 8; + + /** + * Disconnect the user after {@value #IDLE_DISCONNECT} minutes if the client has + * not been making action requests. + */ + public static final int IDLE_DISCONNECT = 5; + + /** + * The maximum server workload. + */ + public static final int USER_LIMIT = 2000; + + /** + * A game tick is a specified measurement of time in which queued events will be + * executed (600 milliseconds). + */ + public static final int TICK_RATE = 600; + + /** + * A toggle for the A* path finder implementation. This is an approximation of + * the distance between two points based on adding the horizontal distance and + * vertical distances rather than computing the exact difference. + */ + public static final boolean MANHATTAN_DISTANCE = true; + + /** + * The public RSA key, used for encrypting passwords. + */ + public static final String RSA_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlPjcLROnibGIGvRp4LuTyS6q7QwAFAyGwO5sqhBl1vL53QBydamS5HT7481OYhGYACjhqbNdylvq5N/wCjff4y24lp0wt42TeeI9iVtFqlFwbhRk4GEqpBoBwkDlcNi8OauooFvN3kTgMgQoN2g0q8dILNijKqZE52qpgZza6IwIDAQAB"; + + /** + * The private RSA key, used for decrypting passwords. + */ + public static final String RSA_PRIVATE_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOU+NwtE6eJsYga9Gngu5PJLqrtDAAUDIbA7myqEGXW8vndAHJ1qZLkdPvjzU5iEZgAKOGps13KW+rk3/AKN9/jLbiWnTC3jZN54j2JW0WqUXBuFGTgYSqkGgHCQOVw2Lw5q6igW83eROAyBCg3aDSrx0gs2KMqpkTnaqmBnNrojAgMBAAECgYALNC42g8i2gzuLPsTDaO9RlDck7dJbbFrCJTehLVGdOntIkr6TRvbjQTWpryK/yoC9scIuGWGyPQTF9qF/cnbS/1WFmifTmh4ZMAIsJ4QzTata3AGMqc7WqwiM30hi6zKedFFOvUg7PGynZ2F/U61E2OIwIqTrpOLuIikkQ6TIiQJBAPmq9rQk7qtISiG+fvj8yO7LKoRtBjF7Fq+jAVB3tlgV/EA15VXmaoY7jMdlTZgtLAZ6SYW2IPXe6uR/IfhVXuUCQQDrDqNEPjRvC0qPS1IB9+SN74fK2bLfQfx9c4qYxbU/FYCMZEBPKPu6B/Ei1TV+VKT9zXwWgfxHq/4ma/MAQpxnAkAvGf3pBn6webbintm9h5Mw2ctvqFHey+X/xLTexXb1L1CjnIdjqVC3ekyY4Ze9+eewYSm1vCKDwEZ4TTPPceuxAkEA0bbKla1GDLivOe+CaD0qDjRiG+pk+2mdArReOHVUgscFXLxo3/d5t300d9ZvlpmgZsy5ZD9uvOdHpjHzqQzFCwJAbNPvG+ZTo9gtC2kOjbTDEYNRsWT8ysM3VMZfVusMdvCxQmpRyWkJdu3e9OHVPLDOrFmiHMpY+gYgxBcdNvVa6w=="; + +} \ No newline at end of file diff --git a/rsc-server/src/org/openrsc/IniFile.java b/rsc-server/src/org/openrsc/IniFile.java new file mode 100644 index 0000000..04174ae --- /dev/null +++ b/rsc-server/src/org/openrsc/IniFile.java @@ -0,0 +1,95 @@ +package org.openrsc; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * NOTE: Not implemented, yet. This will be used to replace Config.java soon + */ +public class IniFile { + + public static void main(String[] args) { + try { + new IniFile("./config.ini"); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private Pattern _section = Pattern.compile("\\s*\\[([^]]*)\\]\\s*"); + private Pattern _keyValue = Pattern.compile("\\s*([^=]*)=(.*)"); + private Map> _entries = new HashMap<>(); + + public IniFile(String path) throws IOException { + load(path); + } + + public void load(String path) throws IOException { + try (BufferedReader br = new BufferedReader(new FileReader(path))) { + String line; + String section = null; + while ((line = br.readLine()) != null) { + Matcher m = _section.matcher(line); + if (m.matches()) { + section = m.group(1).trim(); + } else if (section != null) { + m = _keyValue.matcher(line); + if (m.matches()) { + String key = m.group(1).trim(); + String value = m.group(2).trim(); + Map kv = _entries.get(section); + if (kv == null) { + _entries.put(section, kv = new HashMap<>()); + } + kv.put(key, value); + } + } + } + } + } + + public boolean getBoolean(String section, String key, boolean defaultvalue) { + Map kv = _entries.get(section); + if (kv == null) { + return defaultvalue; + } + return kv.get(key).toLowerCase().equals("true"); + } + + public String getString(String section, String key, String defaultvalue) { + Map kv = _entries.get(section); + if (kv == null) { + return defaultvalue; + } + return kv.get(key); + } + + public int getInt(String section, String key, int defaultvalue) { + Map kv = _entries.get(section); + if (kv == null) { + return defaultvalue; + } + return Integer.parseInt(kv.get(key)); + } + + public float getFloat(String section, String key, float defaultvalue) { + Map kv = _entries.get(section); + if (kv == null) { + return defaultvalue; + } + return Float.parseFloat(kv.get(key)); + } + + public double getDouble(String section, String key, double defaultvalue) { + Map kv = _entries.get(section); + if (kv == null) { + return defaultvalue; + } + return Double.parseDouble(kv.get(key)); + } +} diff --git a/rsc-server/src/org/openrsc/Main.java b/rsc-server/src/org/openrsc/Main.java new file mode 100644 index 0000000..e7d7f3b --- /dev/null +++ b/rsc-server/src/org/openrsc/Main.java @@ -0,0 +1,35 @@ +package org.openrsc; + +import org.openrsc.model.World; +import org.openrsc.model.event.impl.GameTickTaskEvent; +import org.openrsc.net.Server; +import org.openrsc.net.packet.PacketManager; + +/** + * The main class of the OpenRSC Server. + */ +public class Main { + + public static void main(String[] args) { + new Main(); + } + + public Main() { + // Load world + World.getInstance(); + + // Load the packets. + PacketManager.loadPackets(); + + // Bind to network. + try { + Server.getInstance().bind(); + } catch (Exception e) { + e.printStackTrace(); + } + + // Register the tasks / events. + Server.getInstance().submitEvent(new GameTickTaskEvent()); + } + +} diff --git a/rsc-server/src/org/openrsc/model/Combat.java b/rsc-server/src/org/openrsc/model/Combat.java new file mode 100644 index 0000000..427b7b6 --- /dev/null +++ b/rsc-server/src/org/openrsc/model/Combat.java @@ -0,0 +1,25 @@ +package org.openrsc.model; + +import org.openrsc.model.data.Resources; + +/** + * A generic combat system that uses RuneScape Classic rules and calculations. + */ +public class Combat { + + public static void execute(Mob attacker) { + Mob opponent = attacker.getOpponent(); + + if (opponent instanceof Npc) { + Npc npc = (Npc) opponent; + + // Npc is not attackable. + if (!Resources.npcs[npc.getType()].attackable) { + return; + } + + } + + } + +} diff --git a/rsc-server/src/org/openrsc/model/CommandHandler.java b/rsc-server/src/org/openrsc/model/CommandHandler.java new file mode 100644 index 0000000..e6794e7 --- /dev/null +++ b/rsc-server/src/org/openrsc/model/CommandHandler.java @@ -0,0 +1,36 @@ +package org.openrsc.model; + +import org.openrsc.model.player.Player; + +/** + * Handles the execution of commands sent from the client. + */ +public class CommandHandler { + + public static void execute(Player player, String command) throws Exception { + command = command.replaceFirst("/", ""); + + // Check if the command includes multiple requests. + if (command.contains("&")) { + String[] commands = command.replaceAll(" & ", "&").split("&"); + for (int i = 0; i < commands.length; i++) { + execute(player, commands[i]); + } + return; + } + + // /yell command + if (command.startsWith("yell") && !player.isMuted()) { + if (player.hasYellThrottle()) { + player.getPacketDispatcher() + .sendMessage("You can only yell once every " + Constants.YELL_COMMAND_DELAY + " seconds."); + return; + } + // PlayerManager.getInstance().sendGlobalMessage("[Yell]" + + // player.getDisplayName() + ": " + command.substring(5)); + return; + } + + } + +} diff --git a/rsc-server/src/org/openrsc/model/Constants.java b/rsc-server/src/org/openrsc/model/Constants.java new file mode 100644 index 0000000..7e234e5 --- /dev/null +++ b/rsc-server/src/org/openrsc/model/Constants.java @@ -0,0 +1,30 @@ +package org.openrsc.model; + +/** + * Game constants. + */ +public class Constants { + + /** + * The client software build. This is used to reject login requests from + * out-dated clients. + */ + public static final double CLIENT_BUILD = 0.1; + + /** + * This is the maximum distance in which the client will project ray collisions. + */ + public static final int MAXIMUM_INTERACTION_DISTANCE = 48; + + /** + * The amount of time (in seconds) a user must wait between "/yell" command + * executions. + */ + public static final int YELL_COMMAND_DELAY = 15; + + /** + * The default spawn location. + */ + public static final Location DEFAULT_LOCATION = new Location(3200, 3200); + +} diff --git a/rsc-server/src/org/openrsc/model/GameMode.java b/rsc-server/src/org/openrsc/model/GameMode.java new file mode 100644 index 0000000..37fd49c --- /dev/null +++ b/rsc-server/src/org/openrsc/model/GameMode.java @@ -0,0 +1,37 @@ +package org.openrsc.model; + +/** + */ +public enum GameMode { + + REGULAR(0), + + IRONMAN(1), + + HARDCORE_IRONMAN(2), + + ULTIMATE_IRONMAN(3); + + private int value; + + private GameMode(int value) { + this.value = value; + } + + /** + * @return An integer value representing the game mode. + */ + public int getValue() { + return value; + } + + /** + * @param value + * The game mode integer value. + * @return The game mode enum value. + */ + public static GameMode forValue(int value) { + return GameMode.values()[value]; + } + +} diff --git a/rsc-server/src/org/openrsc/model/Location.java b/rsc-server/src/org/openrsc/model/Location.java new file mode 100644 index 0000000..2669005 --- /dev/null +++ b/rsc-server/src/org/openrsc/model/Location.java @@ -0,0 +1,55 @@ +package org.openrsc.model; + +/** + * Represents an in-game location. + */ +public class Location { + + private int x; + private int z; + + /** + * Creates a location instance at 0,0 + */ + public Location() { + this(0, 0); + } + + /** + * Creates a location instance at x,z + */ + public Location(int x, int z) { + this.x = x; + this.z = z; + } + + public void setX(int x) { + this.x = x; + } + + public void setZ(int z) { + this.z = z; + } + + public void set(int x, int z) { + setX(x); + setZ(z); + } + + public int getX() { + return x; + } + + public int getZ() { + return z; + } + + public int getDistance(Location location) { + return Math.abs(location.getX() - location.getX()) + Math.abs(location.getZ() - location.getZ()); + } + + public boolean inBounds(Location location, int distance) { + return getDistance(location) <= distance; + } + +} \ No newline at end of file diff --git a/rsc-server/src/org/openrsc/model/Mob.java b/rsc-server/src/org/openrsc/model/Mob.java new file mode 100644 index 0000000..63c1e9c --- /dev/null +++ b/rsc-server/src/org/openrsc/model/Mob.java @@ -0,0 +1,178 @@ +package org.openrsc.model; + +/** + * A dynamic mobile entity with combat attributes. + */ +public abstract class Mob { + + /** + * A unique id generated for the mob's current session. + */ + private final int sessionId; + + /** + * The mob's current location. + */ + private Location location; + + /** + * The mob's last known location. + */ + private Location travelBack = new Location(-1, -1); + + // Combat mob + private Mob opponent = null; + + // Number of ticks before next attack. + private int combatDelay = 0; + + // Current health. + private int healthCurrent = 10; + + // Maximum health. + private int healthMaximum = 10; + + // Combat level. + private int combatLevel = 0; + + // Create appropriate replacement variables. + public int currentSprite;// TODO + public int nextAnimation;// TODO + public int animationCount[] = new int[12];// TODO + + // TODO A* PATH FINDER + public int stepCount;// TODO + public int movingStep;// TODO + public int waypointCurrent;// TODO + public int waypointsX[] = new int[10];// TODO + public int waypointsZ[] = new int[10];// TODO + + public Mob(int sessionId) { + this(sessionId, 0, 0); + } + + public Mob(int sessionId, int x, int z) { + this.sessionId = sessionId; + this.location = new Location(x, z); + this.travelBack = new Location(x, z); + } + + /** + * We use this to keep the Mob's last known location. + */ + public void setTravelBack() { + travelBack.set(getX(), getZ()); + } + + /** + * Executed every 600 ms. + */ + public abstract void tick(long currentTime); + + /** + * @return True, if the mob is dead. + */ + public abstract boolean isDead(); + + public int getLastX() { + return travelBack.getX(); + } + + public int getLastZ() { + return travelBack.getZ(); + } + + /** + * A unique id generated for the mob's current session. This value is + * incremental and does not get stored. + */ + public int getSessionId() { + return sessionId; + } + + /** + * The entity's location. + */ + public Location getLocation() { + return location; + } + + /** + * The x coordinate of the entity's location. + */ + public int getX() { + return location.getX(); + } + + /** + * The z coordinate of the entity's location. + */ + public int getZ() { + return location.getZ(); + } + + /** + * Sets the entity's location. + * + * @param x + * The new x coordinate. + * @param z + * The new z coordinate. + */ + public void setLocation(int x, int z) { + this.setTravelBack(); + location.set(x, z); + } + + /** + * Sets the entity's location. + * + * @param location + * The new location. + */ + public void setLocation(Location location) { + setLocation(location.getX(), location.getZ()); + } + + public Mob getOpponent() { + return opponent; + } + + public int getCombatDelay() { + return combatDelay; + } + + public void setCombatDelay(int combatDelay) { + this.combatDelay = combatDelay; + } + + public int getHealthCurrent() { + return healthCurrent; + } + + public void setHealthCurrent(int healthCurrent) { + this.healthCurrent = healthCurrent; + } + + public int getHealthMaximum() { + return healthMaximum; + } + + public void setHealthMaximum(int healthMaximum) { + this.healthMaximum = healthMaximum; + } + + public int getCombatLevel() { + return combatLevel; + } + + public void setCombatLevel(int combatLevel) { + this.combatLevel = combatLevel; + } + + // TODO + public int getProjectileRange() { + return 0; + } + +} diff --git a/rsc-server/src/org/openrsc/model/Npc.java b/rsc-server/src/org/openrsc/model/Npc.java new file mode 100644 index 0000000..a46d960 --- /dev/null +++ b/rsc-server/src/org/openrsc/model/Npc.java @@ -0,0 +1,182 @@ +package org.openrsc.model; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Queue; +import java.util.Set; + +import org.openrsc.model.data.Resources; +import org.openrsc.model.player.Player; + +/** + * Represents a non-player game character. + * + */ +public class Npc extends Mob { + + private int type; + + /** + * Where the Npc was spawned. Used for respawn, and retreating. + */ + private Location origin; + + /** + * How far the npc can walk from their origin location. + */ + private int movementRadius = 0; + + /** + * A retreat flag. + */ + private boolean retreatToOrigin = false; + + /** + * A list of nearby players. The npc will only have information for these + * entities. + */ + private final Set playerList = new HashSet<>(); + private final Queue playerRemovalQueue = new LinkedList<>(); + + public Npc(int sessionId, int type, int x, int z) { + super(sessionId); + this.origin = new Location(x, z); + this.setTravelBack(); + + // Initialize the npc + this.init(type); + } + + /** + * Load the Npc definition and attach the data to the mob. + */ + protected void init(int type) { + this.type = type; + /* + * setHealthMaximum(Resources.npcs[type].hits); + * setHealthCurrent(getHealthMaximum()); + * + * // TODO Calculate combat level. int atk = Resources.npcs[type].attack; int + * def = Resources.npcs[type].defense; int str = Resources.npcs[type].strength; + * int cmb = 0; // 1 level in Attack, Strength, Hits or Defense is equal to 1/4 + * of a combat level. // 1 level in Magic or Prayer is equivalent to 1/8 of a + * combat level. // If ranged multiplied by 1.5 is equal to Attack + Strength + * combined, 1 ranged level is worth 0.375 combat levels, and attack and + * strength aren't counted. setCombatLevel(cmb); + */ + } + + /** + * Transforms the npc into a new type. + */ + public void transform(int type) { + init(type); + } + + @Override + public void tick(final long currentTime) { + // Npc is aggressive. + // Loop through nearby players. + // Find a low level player to attack. + if (Resources.npcs[type].isAggressive()) { + // TODO + } + } + + @Override + public boolean isDead() { + return false; + } + + /** + * Loop through each player in the world and create a list of nearby players. + * This allows NPCs to acknowledge and interact with nearby players. + */ + public void updateLocalPlayerList(Set globalPlayerList) { + + // Check if a local player has logged out. + for (Player player : playerList) { + if (!globalPlayerList.contains(player)) { + playerRemovalQueue.add(player); + continue; + } + } + + // Check for new players that should be added to the local list. + boolean isLocal; + for (Player player : globalPlayerList) { + isLocal = getLocation().getDistance(player.getLocation()) < Constants.MAXIMUM_INTERACTION_DISTANCE; + + // Register a new local player. + if (isLocal && !playerList.contains(player)) { + playerList.add(player); + continue; + } + + // Unregister a local player. + if (!isLocal && playerList.contains(player)) { + playerRemovalQueue.add(player); + continue; + } + } + + // Merge the list changes. + while (!playerRemovalQueue.isEmpty()) { + playerList.remove(playerRemovalQueue.poll()); + } + } + + /** + * The list of players which are within interaction distance of the npc. + */ + public Set getLocalPlayers() { + return playerList; + } + + public int getType() { + return type; + } + + /** + * @return How far the Npc can walk from their origin location. + */ + public int getMovementRadius() { + return movementRadius; + } + + /** + * Limit the "AI" travel distance from the origin. + * + * @param movementRadius + * The maximum travel distance. + */ + public void setMovementRadius(int movementRadius) { + this.movementRadius = movementRadius; + } + + /** + * @return + */ + public Location getOriginLocation() { + return origin; + } + + /** + * Forces the Npc to retreat. + */ + public void forceRetreat() { + this.retreatToOrigin = true; + } + + /** + * @return True, if the Npc is retreating. + */ + public boolean isRetreating() { + return retreatToOrigin; + } + + public int getLocalPlayerCount() { + return playerList.size(); + } + +} \ No newline at end of file diff --git a/rsc-server/src/org/openrsc/model/NpcManager.java b/rsc-server/src/org/openrsc/model/NpcManager.java new file mode 100644 index 0000000..fbefd5e --- /dev/null +++ b/rsc-server/src/org/openrsc/model/NpcManager.java @@ -0,0 +1,149 @@ +package org.openrsc.model; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Logger; + +/** + * Manages the npc instances. + */ +public class NpcManager { + + private static final NpcManager INSTANCE = new NpcManager(); + + private final Logger logger = Logger.getLogger(getClass().getName()); + + /** + * An incremental value. Used to generate a unique id for each entity. + */ + private final AtomicInteger uid = new AtomicInteger(0); + + /** + * A list of registered entities. + */ + private final Set currentList = new HashSet(); + + /** + * A list of entities which are pending insertion. + */ + private final Queue insertQueue = new LinkedList<>(); + + /** + * A list of entities which are pending removal. + */ + private final Queue removeQueue = new LinkedList<>(); + + public NpcManager() { + } + + public void tick(long currentTime) { + /* + * Execute the queued insertions. + */ + Npc queuedNpc = null; + while (!insertQueue.isEmpty()) { + queuedNpc = insertQueue.poll(); + currentList.add(queuedNpc); + logger.info("Npc #" + queuedNpc.getSessionId() + " registered."); + } + + /* + * Execute the queued removals. + */ + while (!removeQueue.isEmpty()) { + queuedNpc = removeQueue.poll(); + currentList.remove(queuedNpc); + logger.info("Npc #" + queuedNpc.getSessionId() + " unregistered."); + } + + /* + * Execute the timer based game logic. + */ + for (Npc npc : currentList) { + + // Execute the tick update. + npc.tick(currentTime); + + } + + } + + /** + * Creates a new npc instance. + * + * @return The new npc instance. + */ + public Npc create(int type, int x, int z) { + Npc npc = new Npc(type, uid.getAndIncrement(), x, z); + return npc; + } + + /** + * Adds a npc to the insertion queue. + * + * @param npc + * The npc to register. + */ + public void register(Npc npc) { + insertQueue.add(npc); + } + + /** + * Adds the npc into the removal queue. + * + * @param npc + * The npc to unregister. + */ + public void unregister(Npc npc) { + removeQueue.add(npc); + } + + /** + * @return True, if the list contains the given object. + */ + public boolean contains(Npc npc) { + return currentList.contains(npc); + } + + /** + * @return The npc with the matching sessionId.matching + * sessionId.matching sessionId. + */ + public Npc getForSessionId(int sessionId) { + for (Npc npc : currentList) { + if (npc.getSessionId() == sessionId) { + return npc; + } + } + return null; + } + + /** + * @return A lost of registered npcs. This does not include npcs from the login + * queue. + */ + public Set getList() { + return currentList; + } + + /** + * @return The number of users online. + */ + public int getCount() { + return currentList.size(); + } + + @SuppressWarnings("unused") + public void onShutdown() { + for (Npc npc : currentList) { + } + } + + public static NpcManager getInstance() { + return INSTANCE; + } + +} diff --git a/rsc-server/src/org/openrsc/model/PlayerManager.java b/rsc-server/src/org/openrsc/model/PlayerManager.java new file mode 100644 index 0000000..2b3944d --- /dev/null +++ b/rsc-server/src/org/openrsc/model/PlayerManager.java @@ -0,0 +1,216 @@ +package org.openrsc.model; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.jboss.netty.channel.Channel; +import org.openrsc.Config; +import org.openrsc.model.player.Player; + +/** + * Manages the player instances. + */ +public class PlayerManager { + + private static final PlayerManager INSTANCE = new PlayerManager(); + + private final Logger logger = Logger.getLogger(getClass().getName()); + + /** + * An incremental value. Used to generate a unique id for each entity. + */ + private final AtomicInteger uid = new AtomicInteger(0); + + /** + * A list of registered entities. + */ + private final Set currentList = new HashSet(); + + /** + * A list of entities which are pending insertion. + */ + private final Queue insertQueue = new LinkedList<>(); + + /** + * A list of entities which are pending removal. + */ + private final Queue removeQueue = new LinkedList<>(); + + private int tickCounter = 0; + + public PlayerManager() { + } + + public void tick(long currentTime) { + tickCounter++; + boolean oneMinuteTimeLapse = tickCounter % 100 == 0; + + /* + * OpenRSC doesn't have a 'PID' design like Jagex does for RuneScape. Instead, + * we shuffle the {@link #currentList} to make the order of execution + * unpredictable. + */ + if (oneMinuteTimeLapse) { + List randomList = new ArrayList<>(); + randomList.addAll(currentList); + Collections.shuffle(randomList, new Random()); + currentList.clear(); + currentList.addAll(randomList); + + tickCounter = 0; // Reset tick counter + } + + /* + * Execute the queued insertions. + */ + Player queuedPlayer = null; + while (!insertQueue.isEmpty()) { + queuedPlayer = insertQueue.poll(); + currentList.add(queuedPlayer); + queuedPlayer.executeLogin(); + logger.info(queuedPlayer.getDisplayName() + " registered."); + } + + /* + * Execute the queued removals. + */ + while (!removeQueue.isEmpty()) { + queuedPlayer = removeQueue.poll(); + currentList.remove(queuedPlayer); + queuedPlayer.executeLogout(); + logger.info(queuedPlayer.getDisplayName() + " unregistered."); + } + + /* + * Execute the timer based game logic. + */ + for (Player player : currentList) { + + // Execute the tick update. + player.tick(currentTime); + + // Check for disconnected clients. + if (!player.getChannel().isConnected()) { + logger.info(player.getDisplayName() + " disconnected."); + removeQueue.add(player); + } + + } + + } + + /** + * Creates a new player instance. + * + * @return The new player instance. + */ + public Player create(Channel channel, int databaseId, String displayName) { + Player player = new Player(channel, databaseId, uid.getAndIncrement(), displayName); + return player; + } + + /** + * Adds a player to the login queue. + */ + public void queueLogin(Player player) { + logger.log(Level.INFO, player.getDisplayName() + " added to login queue."); + insertQueue.add(player); + } + + /** + * Adds the player into the logout queue. + */ + public void queueLogout(Player player) { + logger.log(Level.INFO, player.getDisplayName() + " added to logout queue."); + removeQueue.add(player); + } + + /** + * @return True, if the list contains the given object. + */ + public boolean contains(Player player) { + return currentList.contains(player); + } + + /** + * @return The player with the matching accountId. + */ + public Player getForAccountId(int accountId) { + for (Player player : currentList) { + if (player.getAccountId() == accountId) { + return player; + } + } + return null; + } + + /** + * @return The player with the matching sessionId. + */ + public Player getForSessionId(int sessionId) { + for (Player player : currentList) { + if (player.getSessionId() == sessionId) { + return player; + } + } + return null; + } + + /** + * @return A list of registered players. This does not include players from the + * login queue. + */ + public Set getList() { + return currentList; + } + + /** + * @return True, if the world is full. + */ + public boolean isUnderMaximumWorkload() { + return currentList.size() >= Config.USER_LIMIT; + } + + /** + * @return The number of users online. + */ + public int getCount() { + return currentList.size(); + } + + public void onShutdown() { + for (Player player : currentList) { + player.executeLogout(); + } + } + + // TODO Auto-generated method stub + public boolean checkPassword(String username, String password) { + logger.info("TODO"); + return true; + } + + // TODO Auto-generated method stub + private void loadGame(Player user) { + logger.info("TODO"); + } + + // TODO Auto-generated method stub + private void saveGame(Player user) { + logger.info("TODO"); + } + + public static PlayerManager getInstance() { + return INSTANCE; + } + +} diff --git a/rsc-server/src/org/openrsc/model/Privilege.java b/rsc-server/src/org/openrsc/model/Privilege.java new file mode 100644 index 0000000..1e37ddc --- /dev/null +++ b/rsc-server/src/org/openrsc/model/Privilege.java @@ -0,0 +1,55 @@ +package org.openrsc.model; + +/** + * Represents the privileges of a user. + */ +public enum Privilege { + + /** + * A regular user. No special privileges. + */ + REGULAR(0), + + /** + * A player moderator. Can kick, mute regular users. + */ + MODERATOR(1), + + /** + * A regular administrator. Can kick, mute, ban regular users or moderators. + */ + ADMINISTRATOR(2), + + /** + * A root administrator. Can kick, mute, ban, promote any type of user. + */ + ROOT(3), + + /** + * A github contributor. Has no special privileges, but gets a wrench icon near + * their name in-game, as a special thanks for their contribution. + */ + GITHUB_CONTRIBUTOR(4); + + /** + * The integer representing this rights level. + */ + private int value; + + /** + */ + private Privilege(int value) { + this.value = value; + } + + /** + */ + public int toInteger() { + return value; + } + + public static Privilege getForValue(int value) { + return Privilege.values()[value]; + } + +} diff --git a/rsc-server/src/server/game/model/Sector.java b/rsc-server/src/org/openrsc/model/Sector.java similarity index 98% rename from rsc-server/src/server/game/model/Sector.java rename to rsc-server/src/org/openrsc/model/Sector.java index a018786..08ecf53 100644 --- a/rsc-server/src/server/game/model/Sector.java +++ b/rsc-server/src/org/openrsc/model/Sector.java @@ -1,4 +1,4 @@ -package server.game.model; +package org.openrsc.model; import java.io.IOException; import java.nio.ByteBuffer; diff --git a/rsc-server/src/server/game/model/Tile.java b/rsc-server/src/org/openrsc/model/Tile.java similarity index 98% rename from rsc-server/src/server/game/model/Tile.java rename to rsc-server/src/org/openrsc/model/Tile.java index b0f87bb..8a88320 100644 --- a/rsc-server/src/server/game/model/Tile.java +++ b/rsc-server/src/org/openrsc/model/Tile.java @@ -1,4 +1,4 @@ -package server.game.model; +package org.openrsc.model; import java.io.IOException; import java.nio.ByteBuffer; diff --git a/rsc-server/src/org/openrsc/model/World.java b/rsc-server/src/org/openrsc/model/World.java new file mode 100644 index 0000000..816d292 --- /dev/null +++ b/rsc-server/src/org/openrsc/model/World.java @@ -0,0 +1,179 @@ +package org.openrsc.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; +import java.util.zip.ZipFile; + +import org.openrsc.model.data.ResourceLoader; +import org.openrsc.model.data.Resources; +import org.openrsc.model.data.definitions.DoorDef; +import org.openrsc.model.data.definitions.GameObjectDef; +import org.openrsc.model.data.definitions.ItemDef; +import org.openrsc.model.data.definitions.NpcDef; +import org.openrsc.model.data.definitions.PrayerDef; +import org.openrsc.model.data.definitions.SpellDef; +import org.openrsc.model.data.definitions.TileDef; +import org.openrsc.model.data.locations.GameObjectLoc; +import org.openrsc.model.data.locations.ItemLoc; +import org.openrsc.model.data.locations.NpcLoc; + +/** + * Loads game data at startup to create a persistent game world. + */ +public class World { + + private static final World INSTANCE = new World(); + + private static final int SECTORS_X = 1000; + private static final int SECTORS_Z = 1000; + + @SuppressWarnings("unused") + private Sector[][] sectors = new Sector[SECTORS_X][SECTORS_Z]; + + // TODO Use the data for tile collision + @SuppressWarnings("unused") + private List gameObjectLocs = new ArrayList<>(); + + public World() { + Logger.getLogger(getClass().getName()).info("Loading..."); + loadDefinitions(); + loadWorld(); + } + + private void loadDefinitions() { + // Load resources + Resources.doors = (DoorDef[]) ResourceLoader.loadGzipData("defs/DoorDef.xml.gz"); + Resources.gameObjects = (GameObjectDef[]) ResourceLoader.loadGzipData("defs/GameObjectDef.xml.gz"); + Resources.items = (ItemDef[]) ResourceLoader.loadGzipData("defs/ItemDef.xml.gz"); + Resources.npcs = (NpcDef[]) ResourceLoader.loadGzipData("defs/NPCDef.xml.gz"); + Resources.prayers = (PrayerDef[]) ResourceLoader.loadGzipData("defs/PrayerDef.xml.gz"); + Resources.spells = (SpellDef[]) ResourceLoader.loadGzipData("defs/SpellDef.xml.gz"); + Resources.tiles = (TileDef[]) ResourceLoader.loadGzipData("defs/TileDef.xml.gz"); + } + + @SuppressWarnings({ "unused", "unchecked" }) + public void loadWorld() { + + ZipFile tileArchive = ResourceLoader.loadZipData("Landscape.tar.gz"); + + for (int lvl = 0; lvl < 4; lvl++) { + int wildX = 2304; + int wildY = 1776 - (lvl * 944); + for (int sx = 0; sx < 1000; sx += 48) { + for (int sy = 0; sy < 1000; sy += 48) { + int x = (sx + wildX) / 48; + int y = (sy + (lvl * 944) + wildY) / 48; + loadSection(x, y, lvl, getInstance(), sx, sy + (944 * lvl)); + } + } + } + + // Read game object spawns. + this.gameObjectLocs = (List) ResourceLoader.loadGzipData("locs/GameObjectLoc.xml.gz"); + + // TODO These arent implemented yet + // Read ground item spawns. + List itemLocs = (List) ResourceLoader.loadGzipData("locs/ItemLoc.xml.gz"); + for (ItemLoc item : itemLocs) { + // world.registerItem(new Item(item)); + } + itemLocs.clear(); // Unload + + // Read npc spawns. + List npcLocs = (List) ResourceLoader.loadGzipData("locs/NpcLoc.xml.gz"); + for (NpcLoc npc : npcLocs) { + Npc n = NpcManager.getInstance().create(npc.getId(), npc.startX(), npc.startY()); + int movementRadius = new Location(npc.startX(), npc.startY()) + .getDistance(new Location(npc.maxX(), npc.maxY())); + n.setMovementRadius(movementRadius); + } + npcLocs.clear(); // Unload + + } + + private void loadSection(int sectionX, int sectionY, int height, World world, int bigX, int bigY) { + // Sector s = null; + // try { + // String filename = "h" + height + "x" + sectionX + "y" + sectionY; + // ZipEntry e = tileArchive.getEntry(filename); + // if (e == null) { + // throw new Exception("Missing tile: " + filename); + // } + // ByteBuffer data = DataConversions.streamToBuffer( + // new BufferedInputStream(tileArchive.getInputStream(e))); + // s = Sector.unpack(data); + // // s = modifyAndSave(filename, s, bigX, bigY); + // } catch (Exception e) { + // e.printStackTrace(); + // } + // + // for (int y = 0; y < Sector.DEPTH; y++) { + // for (int x = 0; x < Sector.WIDTH; x++) { + // int bx = bigX + x; + // int by = bigY + y; + // if (!world.withinWorld(bx, by)) { + // continue; + // } + // if ((s.getTile(x, y).groundOverlay & 0xff) == 250) { + // s.getTile(x, y).groundOverlay = (byte) 2; + // } + // int groundOverlay = s.getTile(x, y).groundOverlay & 0xFF; + // if (groundOverlay > 0 && Resources + // .getTileDef(groundOverlay - 1).getObjectType() != 0) { + // world.getTileValue(bx, by).mapValue |= 0x40; // 64 + // } + // + // int verticalWall = s.getTile(x, y).verticalWall & 0xFF; + // if (verticalWall > 0 + // && Resources.getDoorDef(verticalWall - 1) + // .getUnknown() == 0 + // && Resources.getDoorDef(verticalWall - 1) + // .getDoorType() != 0) { + // world.getTileValue(bx, by).mapValue |= 1; // 1 + // world.getTileValue(bx, by - 1).mapValue |= 4; // 4 + // } + // + // int horizontalWall = s.getTile(x, y).horizontalWall & 0xFF; + // if (horizontalWall > 0 + // && Resources.getDoorDef(horizontalWall - 1) + // .getUnknown() == 0 + // && Resources.getDoorDef(horizontalWall - 1) + // .getDoorType() != 0) { + // world.getTileValue(bx, by).mapValue |= 2; // 2 + // world.getTileValue(bx - 1, by).mapValue |= 8; // 8 + // } + // + // int diagonalWalls = s.getTile(x, y).diagonalWalls; + // if (diagonalWalls > 0 && diagonalWalls < 12000 + // && Resources.getDoorDef(diagonalWalls - 1) + // .getUnknown() == 0 + // && Resources.getDoorDef(diagonalWalls - 1) + // .getDoorType() != 0) { + // world.getTileValue(bx, by).mapValue |= 0x20; // 32 + // } + // if (diagonalWalls > 12000 && diagonalWalls < 24000 + // && Resources.getDoorDef(diagonalWalls - 12001) + // .getUnknown() == 0 + // && Resources.getDoorDef(diagonalWalls - 12001) + // .getDoorType() != 0) { + // world.getTileValue(bx, by).mapValue |= 0x10; // 16 + // } + // } + // } + } + + /** + * Executed when the server shuts down. + */ + public void onShutdown() { + Logger.getLogger(getClass().getName()).info("Shutting down.."); + PlayerManager.getInstance().onShutdown(); + NpcManager.getInstance().onShutdown(); + } + + public static World getInstance() { + return INSTANCE; + } + +} diff --git a/rsc-server/src/server/res/ResourceLoader.java b/rsc-server/src/org/openrsc/model/data/ResourceLoader.java similarity index 62% rename from rsc-server/src/server/res/ResourceLoader.java rename to rsc-server/src/org/openrsc/model/data/ResourceLoader.java index ad16a87..04ed268 100644 --- a/rsc-server/src/server/res/ResourceLoader.java +++ b/rsc-server/src/org/openrsc/model/data/ResourceLoader.java @@ -1,4 +1,4 @@ -package server.res; +package org.openrsc.model.data; import java.io.File; import java.io.FileOutputStream; @@ -22,14 +22,12 @@ public class ResourceLoader { /** * Package containing entity definitions. */ - private static final String ENTITY_DEF_PACKAGE_NAME = - "server.entityhandling.defs"; + private static final String ENTITY_DEF_PACKAGE_NAME = "org.openrsc.model.data.definitions"; /** * Package containing entity locations. */ - private static final String ENTITY_LOC_PACKAGE_NAME = - "server.entityhandling.locs"; + private static final String ENTITY_LOC_PACKAGE_NAME = "org.openrsc.model.data.locations"; /** * XStream used to read from / write to XML. @@ -38,23 +36,22 @@ public class ResourceLoader { static { // XStream aliases - addAlias("DoorDef", ENTITY_DEF_PACKAGE_NAME + ".DoorDef"); - addAlias("EntityDef" , ENTITY_DEF_PACKAGE_NAME + ".EntityDef"); + addAlias("DoorDef", ENTITY_DEF_PACKAGE_NAME + ".DoorDef"); + addAlias("EntityDef", ENTITY_DEF_PACKAGE_NAME + ".EntityDef"); addAlias("GameObjectDef", ENTITY_DEF_PACKAGE_NAME + ".GameObjectDef"); - addAlias("ItemDef", ENTITY_DEF_PACKAGE_NAME + ".ItemDef"); - addAlias("ItemDropDef", ENTITY_DEF_PACKAGE_NAME + ".ItemDropDef"); - addAlias("NPCDef", ENTITY_DEF_PACKAGE_NAME + ".NpcDef"); - addAlias("PrayerDef", ENTITY_DEF_PACKAGE_NAME + ".PrayerDef"); - addAlias("SpellDef", ENTITY_DEF_PACKAGE_NAME + ".SpellDef"); - addAlias("TileDef", ENTITY_DEF_PACKAGE_NAME + ".TileDef"); + addAlias("ItemDef", ENTITY_DEF_PACKAGE_NAME + ".ItemDef"); + addAlias("ItemDropDef", ENTITY_DEF_PACKAGE_NAME + ".ItemDropDef"); + addAlias("NPCDef", ENTITY_DEF_PACKAGE_NAME + ".NpcDef"); + addAlias("PrayerDef", ENTITY_DEF_PACKAGE_NAME + ".PrayerDef"); + addAlias("SpellDef", ENTITY_DEF_PACKAGE_NAME + ".SpellDef"); + addAlias("TileDef", ENTITY_DEF_PACKAGE_NAME + ".TileDef"); addAlias("GameObjectLoc", ENTITY_LOC_PACKAGE_NAME + ".GameObjectLoc"); - addAlias("ItemLoc", ENTITY_LOC_PACKAGE_NAME + ".ItemLoc"); - addAlias("NPCLoc", ENTITY_LOC_PACKAGE_NAME + ".NpcLoc"); + addAlias("ItemLoc", ENTITY_LOC_PACKAGE_NAME + ".ItemLoc"); + addAlias("NPCLoc", ENTITY_LOC_PACKAGE_NAME + ".NpcLoc"); } public static InputStream getResourceAsStream(String filename) { - return ResourceLoader.class.getClassLoader() - .getResourceAsStream(filename); + return ResourceLoader.class.getClassLoader().getResourceAsStream(filename); } private static void addAlias(String name, String className) { @@ -76,8 +73,7 @@ public static ZipFile loadZipData(String filename) { public static Object loadGzipData(String filename) { try { - InputStream is = new GZIPInputStream( - getResourceAsStream(DATA_DIR + filename)); + InputStream is = new GZIPInputStream(getResourceAsStream(DATA_DIR + filename)); return xStream.fromXML(is); } catch (IOException ex) { ex.printStackTrace(); diff --git a/rsc-server/src/server/res/Resources.java b/rsc-server/src/org/openrsc/model/data/Resources.java similarity index 76% rename from rsc-server/src/server/res/Resources.java rename to rsc-server/src/org/openrsc/model/data/Resources.java index dbd1c8f..3ad2087 100644 --- a/rsc-server/src/server/res/Resources.java +++ b/rsc-server/src/org/openrsc/model/data/Resources.java @@ -1,17 +1,18 @@ -package server.res; +package org.openrsc.model.data; -import server.entityhandling.defs.DoorDef; -import server.entityhandling.defs.GameObjectDef; -import server.entityhandling.defs.ItemDef; -import server.entityhandling.defs.NpcDef; -import server.entityhandling.defs.PrayerDef; -import server.entityhandling.defs.SpellDef; -import server.entityhandling.defs.TileDef; +import org.openrsc.model.data.definitions.DoorDef; +import org.openrsc.model.data.definitions.GameObjectDef; +import org.openrsc.model.data.definitions.ItemDef; +import org.openrsc.model.data.definitions.NpcDef; +import org.openrsc.model.data.definitions.PrayerDef; +import org.openrsc.model.data.definitions.SpellDef; +import org.openrsc.model.data.definitions.TileDef; /** * Class responsible for holding the game's resources. * - *

Based on EntityHandler.java from other RSC sources. + *

+ * Based on EntityHandler.java from other RSC sources. * * @author Dan Bryce */ diff --git a/rsc-server/src/server/entityhandling/defs/DoorDef.java b/rsc-server/src/org/openrsc/model/data/definitions/DoorDef.java similarity index 95% rename from rsc-server/src/server/entityhandling/defs/DoorDef.java rename to rsc-server/src/org/openrsc/model/data/definitions/DoorDef.java index e8f8935..575bd1e 100644 --- a/rsc-server/src/server/entityhandling/defs/DoorDef.java +++ b/rsc-server/src/org/openrsc/model/data/definitions/DoorDef.java @@ -1,4 +1,4 @@ -package server.entityhandling.defs; +package org.openrsc.model.data.definitions; /** * The definition wrapper for doors diff --git a/rsc-server/src/server/entityhandling/defs/EntityDef.java b/rsc-server/src/org/openrsc/model/data/definitions/EntityDef.java similarity index 93% rename from rsc-server/src/server/entityhandling/defs/EntityDef.java rename to rsc-server/src/org/openrsc/model/data/definitions/EntityDef.java index 0a06207..d1c918c 100644 --- a/rsc-server/src/server/entityhandling/defs/EntityDef.java +++ b/rsc-server/src/org/openrsc/model/data/definitions/EntityDef.java @@ -1,4 +1,4 @@ -package server.entityhandling.defs; +package org.openrsc.model.data.definitions; /** * The abstract class EntityDef implements methods for return values which are diff --git a/rsc-server/src/server/entityhandling/defs/GameObjectDef.java b/rsc-server/src/org/openrsc/model/data/definitions/GameObjectDef.java similarity index 96% rename from rsc-server/src/server/entityhandling/defs/GameObjectDef.java rename to rsc-server/src/org/openrsc/model/data/definitions/GameObjectDef.java index 8b5ed0a..85d4dab 100644 --- a/rsc-server/src/server/entityhandling/defs/GameObjectDef.java +++ b/rsc-server/src/org/openrsc/model/data/definitions/GameObjectDef.java @@ -1,4 +1,4 @@ -package server.entityhandling.defs; +package org.openrsc.model.data.definitions; /** * The definition wrapper for game objects diff --git a/rsc-server/src/server/entityhandling/defs/ItemDef.java b/rsc-server/src/org/openrsc/model/data/definitions/ItemDef.java similarity index 95% rename from rsc-server/src/server/entityhandling/defs/ItemDef.java rename to rsc-server/src/org/openrsc/model/data/definitions/ItemDef.java index 75d9bd7..2ff4693 100644 --- a/rsc-server/src/server/entityhandling/defs/ItemDef.java +++ b/rsc-server/src/org/openrsc/model/data/definitions/ItemDef.java @@ -1,4 +1,4 @@ -package server.entityhandling.defs; +package org.openrsc.model.data.definitions; /** * The definition wrapper for items diff --git a/rsc-server/src/server/entityhandling/defs/ItemDropDef.java b/rsc-server/src/org/openrsc/model/data/definitions/ItemDropDef.java similarity index 85% rename from rsc-server/src/server/entityhandling/defs/ItemDropDef.java rename to rsc-server/src/org/openrsc/model/data/definitions/ItemDropDef.java index ececdc0..9c75ff8 100644 --- a/rsc-server/src/server/entityhandling/defs/ItemDropDef.java +++ b/rsc-server/src/org/openrsc/model/data/definitions/ItemDropDef.java @@ -1,6 +1,7 @@ -package server.entityhandling.defs; +package org.openrsc.model.data.definitions; public class ItemDropDef { + public int id; public int amount; public int weight; @@ -16,4 +17,5 @@ public int getAmount() { public int getWeight() { return weight; } + } \ No newline at end of file diff --git a/rsc-server/src/server/entityhandling/defs/NpcDef.java b/rsc-server/src/org/openrsc/model/data/definitions/NpcDef.java similarity index 98% rename from rsc-server/src/server/entityhandling/defs/NpcDef.java rename to rsc-server/src/org/openrsc/model/data/definitions/NpcDef.java index 8ba8cc5..ee97b63 100644 --- a/rsc-server/src/server/entityhandling/defs/NpcDef.java +++ b/rsc-server/src/org/openrsc/model/data/definitions/NpcDef.java @@ -1,4 +1,4 @@ -package server.entityhandling.defs; +package org.openrsc.model.data.definitions; /** * The definition wrapper for npcs diff --git a/rsc-server/src/server/entityhandling/defs/PrayerDef.java b/rsc-server/src/org/openrsc/model/data/definitions/PrayerDef.java similarity index 69% rename from rsc-server/src/server/entityhandling/defs/PrayerDef.java rename to rsc-server/src/org/openrsc/model/data/definitions/PrayerDef.java index dcec74c..20496cf 100644 --- a/rsc-server/src/server/entityhandling/defs/PrayerDef.java +++ b/rsc-server/src/org/openrsc/model/data/definitions/PrayerDef.java @@ -1,4 +1,4 @@ -package server.entityhandling.defs; +package org.openrsc.model.data.definitions; /** * The definition wrapper for prayers @@ -10,7 +10,8 @@ public class PrayerDef extends EntityDef { */ public int reqLevel; /** - * The drain rate of the prayer (perhaps points per min?) + * The drain rate of the prayer (perhaps points per min?) This is likely per + * game tick - morgue */ public int drainRate; diff --git a/rsc-server/src/server/entityhandling/defs/SpellDef.java b/rsc-server/src/org/openrsc/model/data/definitions/SpellDef.java similarity index 95% rename from rsc-server/src/server/entityhandling/defs/SpellDef.java rename to rsc-server/src/org/openrsc/model/data/definitions/SpellDef.java index b661a11..6ca4a27 100644 --- a/rsc-server/src/server/entityhandling/defs/SpellDef.java +++ b/rsc-server/src/org/openrsc/model/data/definitions/SpellDef.java @@ -1,4 +1,4 @@ -package server.entityhandling.defs; +package org.openrsc.model.data.definitions; import java.util.HashMap; import java.util.Map.Entry; diff --git a/rsc-server/src/server/entityhandling/defs/TileDef.java b/rsc-server/src/org/openrsc/model/data/definitions/TileDef.java similarity index 86% rename from rsc-server/src/server/entityhandling/defs/TileDef.java rename to rsc-server/src/org/openrsc/model/data/definitions/TileDef.java index 7eabecf..79945de 100644 --- a/rsc-server/src/server/entityhandling/defs/TileDef.java +++ b/rsc-server/src/org/openrsc/model/data/definitions/TileDef.java @@ -1,4 +1,4 @@ -package server.entityhandling.defs; +package org.openrsc.model.data.definitions; public class TileDef { public int colour; diff --git a/rsc-server/src/server/entityhandling/locs/GameObjectLoc.java b/rsc-server/src/org/openrsc/model/data/locations/GameObjectLoc.java similarity index 87% rename from rsc-server/src/server/entityhandling/locs/GameObjectLoc.java rename to rsc-server/src/org/openrsc/model/data/locations/GameObjectLoc.java index a35d052..68dca74 100644 --- a/rsc-server/src/server/entityhandling/locs/GameObjectLoc.java +++ b/rsc-server/src/org/openrsc/model/data/locations/GameObjectLoc.java @@ -1,5 +1,8 @@ -package server.entityhandling.locs; +package org.openrsc.model.data.locations; +/** + * Represents a static game object location. + */ public class GameObjectLoc { /** * The id of the gameObject diff --git a/rsc-server/src/server/entityhandling/locs/ItemLoc.java b/rsc-server/src/org/openrsc/model/data/locations/ItemLoc.java similarity index 87% rename from rsc-server/src/server/entityhandling/locs/ItemLoc.java rename to rsc-server/src/org/openrsc/model/data/locations/ItemLoc.java index 2752ebe..a401f3b 100644 --- a/rsc-server/src/server/entityhandling/locs/ItemLoc.java +++ b/rsc-server/src/org/openrsc/model/data/locations/ItemLoc.java @@ -1,5 +1,8 @@ -package server.entityhandling.locs; +package org.openrsc.model.data.locations; +/** + * Represents a static ground item spawn. + */ public class ItemLoc { /** * The id of the gameObject diff --git a/rsc-server/src/server/entityhandling/locs/NpcLoc.java b/rsc-server/src/org/openrsc/model/data/locations/NpcLoc.java similarity index 90% rename from rsc-server/src/server/entityhandling/locs/NpcLoc.java rename to rsc-server/src/org/openrsc/model/data/locations/NpcLoc.java index 1649053..275b744 100644 --- a/rsc-server/src/server/entityhandling/locs/NpcLoc.java +++ b/rsc-server/src/org/openrsc/model/data/locations/NpcLoc.java @@ -1,5 +1,8 @@ -package server.entityhandling.locs; +package org.openrsc.model.data.locations; +/** + * Represents a static npc item spawn. + */ public class NpcLoc { /** * The id of the Npc diff --git a/rsc-server/src/org/openrsc/model/event/Event.java b/rsc-server/src/org/openrsc/model/event/Event.java new file mode 100644 index 0000000..90300a4 --- /dev/null +++ b/rsc-server/src/org/openrsc/model/event/Event.java @@ -0,0 +1,76 @@ +package org.openrsc.model.event; + +import org.openrsc.task.TaskEngine; + +/** + * Represents a task that is executed in the future, once, or periodically. + */ +public abstract class Event { + + /** + * The delay (in milliseconds). + */ + private long delay; + + /** + * The running flag. + */ + private boolean running = true; + + /** + * Creates an event with the specified delay. + * + * @param delay + * The delay. + */ + public Event(long delay) { + this.delay = delay; + } + + /** + * Gets the event delay. + * + * @return The delay, in milliseconds. + */ + public long getDelay() { + return delay; + } + + /** + * Sets the event delay. + * + * @param delay + * The delay to set. + * @throws IllegalArgumentException + * if the delay is negative. + */ + public void setDelay(long delay) { + if (delay < 0) { + throw new IllegalArgumentException("Delay must be positive."); + } + this.delay = delay; + } + + /** + * Checks if the event is running. + * + * @return True, if the event is still running. + */ + public boolean isRunning() { + return running; + } + + /** + * Stops the event from running in the future. + */ + public void stop() { + running = false; + } + + /** + * The execute method is called when the event is run. The general contract of + * the execute method is that it may take any action whatsoever. + */ + public abstract void execute(TaskEngine context); + +} diff --git a/rsc-server/src/org/openrsc/model/event/EventManager.java b/rsc-server/src/org/openrsc/model/event/EventManager.java new file mode 100644 index 0000000..1f7ce87 --- /dev/null +++ b/rsc-server/src/org/openrsc/model/event/EventManager.java @@ -0,0 +1,51 @@ +package org.openrsc.model.event; + +import java.util.concurrent.TimeUnit; + +import org.openrsc.task.TaskEngine; + +/** + */ +public class EventManager { + + private final TaskEngine engine; + + public EventManager(TaskEngine engine) { + this.engine = engine; + } + + /** + * Submits a new event. + * + * @param event + * The event to submit. + */ + public void submit(Event event) { + submit(event, event.getDelay()); + } + + /** + * Schedules an event to run after the specified delay. + * + * @param event + * The event. + * @param delay + * The delay. + */ + private void submit(Event event, long delay) { + engine.scheduleLogic(new Runnable() { + @Override + public void run() { + if (!event.isRunning()) { + return; + } + long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); + event.execute(engine); + long elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start; + long remaining = event.getDelay() - elapsed; + submit(event, remaining < 0 ? 0 : remaining); + } + }, delay, TimeUnit.MILLISECONDS); + } + +} \ No newline at end of file diff --git a/rsc-server/src/org/openrsc/model/event/impl/GameTickTaskEvent.java b/rsc-server/src/org/openrsc/model/event/impl/GameTickTaskEvent.java new file mode 100644 index 0000000..028c0fe --- /dev/null +++ b/rsc-server/src/org/openrsc/model/event/impl/GameTickTaskEvent.java @@ -0,0 +1,40 @@ +package org.openrsc.model.event.impl; + +import java.util.concurrent.TimeUnit; + +import org.openrsc.model.NpcManager; +import org.openrsc.model.PlayerManager; +import org.openrsc.model.event.Event; +import org.openrsc.task.Task; +import org.openrsc.task.TaskEngine; + +/** + * The game tick event task. + */ +public class GameTickTaskEvent extends Event { + + public GameTickTaskEvent() { + super(600); + } + + @Override + public void execute(TaskEngine context) { + context.pushTask(new UpdateTask()); + } + +} + +class UpdateTask implements Task { + + @Override + public void execute(TaskEngine context) { + context.submitTask(new Runnable() { + public void run() { + long currentTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); + PlayerManager.getInstance().tick(currentTime); + NpcManager.getInstance().tick(currentTime); + } + }); + } + +} \ No newline at end of file diff --git a/rsc-server/src/org/openrsc/model/net/GameLoginHandler.java b/rsc-server/src/org/openrsc/model/net/GameLoginHandler.java new file mode 100644 index 0000000..7a2804e --- /dev/null +++ b/rsc-server/src/org/openrsc/model/net/GameLoginHandler.java @@ -0,0 +1,226 @@ +package org.openrsc.model.net; + +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.Random; + +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.ChannelFutureListener; +import org.openrsc.Config; +import org.openrsc.model.Constants; +import org.openrsc.model.PlayerManager; +import org.openrsc.model.Privilege; +import org.openrsc.model.player.Player; +import org.openrsc.net.packet.Packet; +import org.openrsc.util.GameUtils; + +/** + * Handles a client login request. Validates the account credentials and checks + * for special world parameters, etc. + */ +public class GameLoginHandler { + + public GameLoginHandler() { + // .. + } + + private String usernameErrorReport = null; + private String passwordErrorReport = null; + + public void execute(Channel channel, Packet packet) { + + // Check the client build. + double clientBuild = packet.getDouble(); + + // Read account credentials from the client. + String username = packet.getString(); + + // Read the secure password from the client. + String password = packet.getString(); // TODO Use RSA + + // Invalid client build. + if (clientBuild != Constants.CLIENT_BUILD) { + sendLoginError(channel, "Please update your client"); + return; + } + + // Server is full. + if (PlayerManager.getInstance().getCount() >= Config.USER_LIMIT) { + sendLoginError(channel, "Server is full"); + return; + } + + // Username is not allowed. + if (!isUsernameValid(username)) { + sendLoginError(channel, usernameErrorReport); + return; + } + + // Password is not allowed. + if (!isPasswordValid(password)) { + sendLoginError(channel, passwordErrorReport); + return; + } + + // Account is not registered. + if (!doesUserExist(username)) { + sendLoginError(channel, "Account does not exist"); + return; + } + + // Account is registered. + // Get the database uuid. + final int databaseId = new Random().nextInt(9999); // TODO load from database + + // Invalid username or password. + if (!validateCredentials(databaseId, password)) { + sendLoginError(channel, "Invalid username or password"); + return; + } + + // The client's password is correct. + // Get the display name. + final String displayName = username; // TODO could be used for display name + + // Account is already logged in. + if (isLoggedIn(databaseId)) { + sendLoginError(channel, "Account is already logged in"); + return; + } + + // Credentials are valid. Load the account privileges. + Privilege privilege = getPrivilege(databaseId); + // System.out.println("Login->privilege=" + privilege); + + // Account is banned. + if (isBanned(databaseId)) { + sendLoginError(channel, "Account is banned"); + return; + } + + /* + * Successful login. + */ + + // Create the player instance. + final Player player = PlayerManager.getInstance().create(channel, databaseId, displayName); + player.setPrivileges(privilege); + + // Send the login response to the client. + Packet success = new Packet(2).putBoolean(true).putBase37(displayName).putInt(player.getSessionId()) + .putByte(player.getPrivileges().toInteger()); + channel.write(success).addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) { + // Register the player. + PlayerManager.getInstance().queueLogin(player); + } + }); + + } + + /** + * The login request has been denied. Tell the client which error, then close + * the connection. + */ + private void sendLoginError(Channel channel, String message) { + Packet packet = new Packet(2).putBoolean(false).putString(message); + channel.write(packet).addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) { + channel.close(); + } + }); + } + + /** + * Checks if the username String is valid. + */ + private boolean isUsernameValid(String username) { + username = username.replaceAll("_", " "); + CharacterIterator iterator = new StringCharacterIterator(username); + for (char ch = iterator.first(); ch != CharacterIterator.DONE; ch = iterator.next()) { + if (!GameUtils.isValidCharacter(ch)) { + usernameErrorReport = "Username can only contain a-z 0-9."; + return false; + } + } + if (username.startsWith(" ")) { + usernameErrorReport = "Username cannot start with spaces."; + return false; + } + if (username.endsWith(" ")) { + usernameErrorReport = "Username cannot end with spaces."; + return false; + } + if (username.contains(" ")) { + usernameErrorReport = "Cannot contain back to back spaces."; + return false; + } + if (username.length() < 3) { + usernameErrorReport = "Username too short."; + return false; + } + if (username.length() > 12) { + usernameErrorReport = "Username too long."; + return false; + } + return true; + } + + /** + * Checks if the password String is valid. + */ + private boolean isPasswordValid(String password) { + CharacterIterator iterator = new StringCharacterIterator(password); + for (char ch = iterator.first(); ch != CharacterIterator.DONE; ch = iterator.next()) { + if (!GameUtils.isValidCharacter(ch)) { + passwordErrorReport = "Password contains invalid characters."; + return false; + } + } + if (password.length() < 5) { + passwordErrorReport = "Password too short."; + return false; + } + if (password.length() > 25) { + passwordErrorReport = "Password too long."; + return false; + } + return true; + } + + // Check if account has completed registration. + private boolean doesUserExist(String username) { + // TODO Auto-generated method stub + return true; + } + + // Check if username password combination is valid. + private boolean validateCredentials(int databaseId, String password) { + // TODO Auto-generated method stub + return true; + } + + // Check if user is already logged in. + private boolean isLoggedIn(int databaseId) { + boolean connected = PlayerManager.getInstance().getForAccountId(databaseId) != null; + + // TODO : Check if player is logged in to a different server + return connected; + } + + // Check if account is banned. + private boolean isBanned(int databaseId) { + // TODO Auto-generated method stub + return false; + } + + // Get user privilege. + private Privilege getPrivilege(int databaseId) { + // TODO Auto-generated method stub + return Privilege.GITHUB_CONTRIBUTOR; + } + +} \ No newline at end of file diff --git a/rsc-server/src/org/openrsc/model/net/packet/SilentPacket.java b/rsc-server/src/org/openrsc/model/net/packet/SilentPacket.java new file mode 100644 index 0000000..b4e34cb --- /dev/null +++ b/rsc-server/src/org/openrsc/model/net/packet/SilentPacket.java @@ -0,0 +1,46 @@ +package org.openrsc.model.net.packet; + +import org.openrsc.model.player.Player; +import org.openrsc.net.packet.Packet; +import org.openrsc.net.packet.PacketHandler; + +/** + * There is no server reaction, no events, no response, etc. The client only + * sends these packets to prevent an idle logout. + */ +public class SilentPacket implements PacketHandler { + + /** + * Sent from the client once per second, if the user has touched a keyboard key + * while the game window is in the foreground. + */ + private final int KEYBOARD_TOUCH = 0; + + /** + * Sent from the client once per second, if the mouse cursor has moved, or if a + * mouse button has been clicked, while inside of the game window. + */ + private final int MOUSE_MOVEMENT = 1; + + /** + * Sent from client once per second, if the camera has moved. + */ + private final int CAMERA_UPDATE = 2; + + @Override + public void execute(Player player, Packet packet) { + int resetIndex = packet.getByte(); + switch (resetIndex) { + case KEYBOARD_TOUCH: + case MOUSE_MOVEMENT: + case CAMERA_UPDATE: + break; + } + } + + @Override + public boolean addToQueue() { + return false; + } + +} \ No newline at end of file diff --git a/rsc-server/src/org/openrsc/model/player/PacketDispatcher.java b/rsc-server/src/org/openrsc/model/player/PacketDispatcher.java new file mode 100644 index 0000000..4e780a3 --- /dev/null +++ b/rsc-server/src/org/openrsc/model/player/PacketDispatcher.java @@ -0,0 +1,27 @@ +package org.openrsc.model.player; + +import org.openrsc.net.packet.Packet; + +/** + * Contains the outgoing packets. Each packet can be be given specific + * parameters. + */ +public class PacketDispatcher { + + /** + * The player who receives the outgoing packet. + */ + private final Player player; + + public PacketDispatcher(final Player player) { + this.player = player; + } + + public void sendMessage(String string) { + Packet packet = new Packet(3); + packet.putByte(player.getPrivileges().toInteger()); + packet.putString(string); + player.getChannel().write(packet); + } + +} \ No newline at end of file diff --git a/rsc-server/src/org/openrsc/model/player/Player.java b/rsc-server/src/org/openrsc/model/player/Player.java new file mode 100644 index 0000000..7e25c5b --- /dev/null +++ b/rsc-server/src/org/openrsc/model/player/Player.java @@ -0,0 +1,246 @@ +package org.openrsc.model.player; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import org.jboss.netty.channel.Channel; +import org.openrsc.Config; +import org.openrsc.model.Constants; +import org.openrsc.model.GameMode; +import org.openrsc.model.Mob; +import org.openrsc.model.Npc; +import org.openrsc.model.NpcManager; +import org.openrsc.model.PlayerManager; +import org.openrsc.model.Privilege; +import org.openrsc.net.packet.Packet; +import org.openrsc.net.packet.PacketManager; +import org.openrsc.util.GameUtils; + +public class Player extends Mob { + + // Packet Handling / Network + private final Channel channel; + private final BlockingQueue packetQueue = new LinkedBlockingQueue<>(); + private final PacketDispatcher packetDispatcher; + private long lastPacketReceived; + + /** + * The account id is an incremental value created by the database. It is + * permanent and will transfer between sessions. + */ + private int accountId; + + /** + * The session id is an incremental value generated by the server. It is a + * temporary value that regenerates upon login. + */ + private int sessionId; + + /** + * The account display name. + */ + private final String displayName; + + /** + * The account privilege. + */ + private Privilege privilege; + + /** + * The user's game mode. + */ + private GameMode gameMode; + + /** + */ + private boolean isMuted = false; + + /** + * A list of nearby players. The client will only have information for these + * entities. + */ + private Set localPlayerList = null; + + /** + * A list of nearby npcs. The client will only have information for these + * entities. + */ + private Set localNpcList = null; + + private long lastYellTime = 0L; + + // TODO Appearance + public int colourHairType; + public int colourTopType; + public int colourBottomType; + public int colourSkinType; + + /** + * Npc pet slot. + */ + private Npc familiar; + + public Player(Channel channel, int accountId, int sessionId, String displayName) { + super(sessionId, Constants.DEFAULT_LOCATION.getX(), Constants.DEFAULT_LOCATION.getZ()); + this.accountId = accountId; + this.sessionId = sessionId; + this.displayName = displayName; + this.channel = channel; + channel.setAttachment(this); + this.packetDispatcher = new PacketDispatcher(this); + this.lastPacketReceived = GameUtils.getCurrentTimeMillis(); + } + + @Override + public void tick(long currentTime) { + // Execute the queued packets. + List toProcess = new ArrayList<>(); + packetQueue.drainTo(toProcess); + for (Packet packet : toProcess) { + PacketManager.execute(this, packet); + packetQueue.remove(packet); + } + + // Idle logout timer. + if (currentTime - lastPacketReceived > (60_000 * Config.IDLE_DISCONNECT)) { + System.out.println("[" + displayName + "]Disconnected - Idle logout."); + PlayerManager.getInstance().queueLogout(this); + return; + } + + } + + @Override + public boolean isDead() { + return false; + } + + /** + * Executed when the player gets registered. + */ + public void executeLogin() { + super.setLocation(Constants.DEFAULT_LOCATION); + super.setTravelBack(); + packetDispatcher.sendMessage("Welcome to RuneScape."); + } + + /** + * Executed when the player gets unregistered. + */ + public void executeLogout() { + packetQueue.clear(); + interrupt(); + if (familiar != null) { + NpcManager.getInstance().unregister(familiar); + } + if (channel.isConnected()) { + channel.disconnect(); + } + } + + /** + * Queues an incoming packet to be executed in the next game tick. + */ + public void addQueuedPacket(Packet packet) { + this.packetQueue.add(packet); + } + + /** + */ + public void interrupt() { + // Reset queued actions. + } + + /** + * A cooldown check for the /yell command. + * + * @return False if there is no cooldown. + */ + public boolean hasYellThrottle() { + if (isDeveloper() || isModerator()) { + return false; + } + long currentTimeMillis = GameUtils.getCurrentTimeMillis(); + if (currentTimeMillis - lastYellTime > (Constants.YELL_COMMAND_DELAY * 1000)) { + lastYellTime = currentTimeMillis; + return false; + } + return true; + } + + /** + * The list of players which are visible client-side. + */ + public Set getLocalPlayerList() { + return localPlayerList; + } + + /** + * The list of npcs which are visible client-side. + */ + public Set getLocalNpcList() { + return localNpcList; + } + + public Channel getChannel() { + return channel; + } + + public PacketDispatcher getPacketDispatcher() { + return packetDispatcher; + } + + public void updateLastPacketReceivedTime() { + this.lastPacketReceived = GameUtils.getCurrentTimeMillis(); + } + + public int getAccountId() { + return accountId; + } + + public int getSessionId() { + return sessionId; + } + + public String getDisplayName() { + return displayName; + } + + public Privilege getPrivileges() { + return privilege; + } + + public void setPrivileges(Privilege privilege) { + this.privilege = privilege; + } + + /** + * @return True, if the account has developer privileges. + */ + public boolean isDeveloper() { + return privilege == Privilege.GITHUB_CONTRIBUTOR || privilege == Privilege.ADMINISTRATOR + || privilege == Privilege.ROOT; + } + + /** + * @return True, if the account has moderator privileges. + */ + public boolean isModerator() { + return privilege != Privilege.REGULAR; + } + + public GameMode getGameMode() { + return gameMode; + } + + /** + * @return True, if chatbox communication privileges are suspended. + */ + public boolean isMuted() { + return isMuted; + } + +} \ No newline at end of file diff --git a/rsc-server/src/org/openrsc/net/ConnectionHandler.java b/rsc-server/src/org/openrsc/net/ConnectionHandler.java new file mode 100644 index 0000000..b9da145 --- /dev/null +++ b/rsc-server/src/org/openrsc/net/ConnectionHandler.java @@ -0,0 +1,138 @@ +package org.openrsc.net; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.ExceptionEvent; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelHandler; +import org.openrsc.Config; +import org.openrsc.model.PlayerManager; +import org.openrsc.model.net.GameLoginHandler; +import org.openrsc.model.player.Player; +import org.openrsc.net.packet.Packet; +import org.openrsc.net.packet.PacketManager; + +/** + * The ConnectionHandler handles incoming packet data. + */ +public class ConnectionHandler extends SimpleChannelHandler { + + private ArrayList connections = new ArrayList(); + + /** + * The login decoder instance. + */ + private final GameLoginHandler loginDecoder; + + public ConnectionHandler() { + this.loginDecoder = new GameLoginHandler(); + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent event) { + Packet packet = (Packet) event.getMessage(); + if (packet == null) { + Logger.getLogger(getClass().getName()).log(Level.WARNING, "Received null packet from client."); + return; + } + + final int opcode = packet.getOpcode(); + + // XXX Opcode 0 is reserved for future pre-login handshake. + if (opcode == 0) { + return; + } + + // Ping Packet + if (opcode == 1) { + // When the client sent the ping. + ctx.getChannel().write(new Packet(1)); + return; + } + + // Login packet + if (opcode == 2) { + loginDecoder.execute(ctx.getChannel(), packet); + return; + } + + // Get player instance. + Player player = ((Player) ctx.getChannel().getAttachment()); + if (player == null) { + Logger.getLogger(getClass().getName()).log(Level.WARNING, "Received packet from null player."); + return; + } + + // Check if the packet exists. + if (PacketManager.get(opcode) == null) { + Logger.getLogger(getClass().getName()).log(Level.WARNING, "No packet found for " + opcode); + return; + } + + // Keep the player from idle logout. + player.updateLastPacketReceivedTime(); + + // Queued packet. + // Execute in the next game tick. + if (PacketManager.get(opcode).addToQueue()) { + player.addQueuedPacket(packet); + return; + } + + // Reactor-based packet. + // Execute immediately. + PacketManager.get(opcode).execute(player, packet); + } + + @Override + public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent event) { + String address = ctx.getChannel().getRemoteAddress().toString().split(":")[0]; + int count = 1; + for (int i = 0; i < connections.size(); i++) { + if (connections.get(i).equalsIgnoreCase(address)) { + count++; + } + } + if (count > Config.CONNECTION_LIMIT) { + ctx.getChannel().close(); + return; + } + Logger.getLogger(getClass().getName()).log(Level.INFO, + "Connection [" + count + "/" + Config.CONNECTION_LIMIT + "] accepted from " + address); + connections.add(address); + } + + @Override + public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent event) { + Player player = (Player) ctx.getChannel().getAttachment(); + if (PlayerManager.getInstance().contains(player)) { + PlayerManager.getInstance().queueLogout(player); + } + String address = ctx.getChannel().getRemoteAddress().toString().split(":")[0]; + if (connections.contains(address)) { + connections.remove(address); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent event) { + if (!(event.getCause() instanceof IOException)) { + Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Exception caught: ", event.getCause()); + return; + } + Player player = (Player) ctx.getAttachment(); + if (PlayerManager.getInstance().contains(player)) { + PlayerManager.getInstance().queueLogout(player); + } + String address = ctx.getChannel().getRemoteAddress().toString().split(":")[0]; + if (connections.contains(address)) { + connections.remove(address); + } + } + +} diff --git a/rsc-server/src/org/openrsc/net/Server.java b/rsc-server/src/org/openrsc/net/Server.java new file mode 100644 index 0000000..1878f50 --- /dev/null +++ b/rsc-server/src/org/openrsc/net/Server.java @@ -0,0 +1,123 @@ +package org.openrsc.net; + +import static org.jboss.netty.channel.Channels.pipeline; + +import java.net.InetSocketAddress; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.jboss.netty.bootstrap.ServerBootstrap; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; +import org.openrsc.Config; +import org.openrsc.model.World; +import org.openrsc.model.event.Event; +import org.openrsc.model.event.EventManager; +import org.openrsc.net.codec.PacketDecoder; +import org.openrsc.net.codec.PacketEncoder; +import org.openrsc.task.TaskEngine; + +/** + * Integrates the Netty api into the OpenRSC project. The server class is tied + * together with the task event and event manager. + */ +public class Server { + + private static final Server INSTANCE = new Server(); + + /** + * Creates a new instance. Calling this constructor is same with calling + * NioServerSocketChannelFactory(Executor, Executor, int) with 2 * the number of + * available processors in the machine. The number of available processors is + * obtained by Runtime.availableProcessors(). + * https://docs.jboss.org/netty/3.2/api/org/jboss/netty/channel/socket/nio/NioServerSocketChannelFactory.html + */ + private final ServerBootstrap SERVER_BOOTSTRAP = new ServerBootstrap(new NioServerSocketChannelFactory( + Config.NETTY_BOSS_EXECUTOR, Config.NETTY_WORK_EXECUTOR, Config.NETTY_MAXIMUM_WORKER_COUNT)); + + private final TaskEngine taskEngine; + private final EventManager eventManager; + + public Server() { + // Initialize the main thread. + this.taskEngine = new TaskEngine(); + + // Initialize the event manager. + this.eventManager = new EventManager(taskEngine); + } + + public void bind() throws Exception { + Logger.getLogger(getClass().getName()).info("Binding to port " + Config.SERVER_PORT + ".."); + + try { + // The ChannelPipelineFactory creates a new ChannelPipeline for each new + // Channel. + SERVER_BOOTSTRAP.setPipelineFactory((new ChannelPipelineFactory() { + @Override + public ChannelPipeline getPipeline() throws Exception { + ChannelPipeline pipeline = pipeline(); + /* + * Add the packet encoder/decoder network protocol. + */ + pipeline.addLast("decoder", new PacketDecoder()); + pipeline.addLast("encoder", new PacketEncoder()); + + /* + * Handles or intercepts network data and forwards it to the next handler in a + * ChannelPipeline. + */ + pipeline.addLast("handler", new ConnectionHandler()); + return pipeline; + } + })); + + // https://en.wikipedia.org/wiki/Nagles_algorithm + SERVER_BOOTSTRAP.setOption("tcpNoDelay", true); + + // Bind the server bootstrap to the specified port. + SERVER_BOOTSTRAP.bind(new InetSocketAddress(Config.SERVER_PORT)); + Logger.getLogger(getClass().getName()).info("Server is ONLINE!"); + } catch (Exception e) { + Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Startup aborted due to network error.", e); + throw new Exception("Startup aborted due to network error."); + } + + // The network bind was successful. + // Start the task engine. + taskEngine.start(); + } + + public EventManager getEventManager() { + return eventManager; + } + + /** + * Submits a new event to the event manager. + * + * @param task + */ + public void submitEvent(Event event) { + eventManager.submit(event); + } + + public void shutdown(boolean error) { + Logger.getLogger(getClass().getName()).info("Shutting down.."); + + // Shutdown the network. + SERVER_BOOTSTRAP.shutdown(); + + World.getInstance().onShutdown(); + + // Stop the task engine. + taskEngine.stop(); + + // Terminate the jvm. + System.exit(error ? 1 : 0); + } + + public static Server getInstance() { + return INSTANCE; + } + +} diff --git a/rsc-server/src/org/openrsc/net/codec/PacketDecoder.java b/rsc-server/src/org/openrsc/net/codec/PacketDecoder.java new file mode 100644 index 0000000..4a16244 --- /dev/null +++ b/rsc-server/src/org/openrsc/net/codec/PacketDecoder.java @@ -0,0 +1,52 @@ +package org.openrsc.net.codec; + +import org.jboss.netty.channel.Channel; +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.handler.codec.frame.FrameDecoder; +import org.openrsc.net.packet.Packet; + +/** + * Reads an incoming packet. + */ +public class PacketDecoder extends FrameDecoder { + + @Override + protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception { + // Make sure the length field was received. + if (buffer.readableBytes() < 4) { + // The length field was not received yet - return null. + // This method will be invoked again when more packets are + // received and appended to the buffer. + return null; + } + + // Mark the current buffer position before reading the length field + // because the whole frame might not be in the buffer yet. + // We will reset the buffer position to the marked position if + // there's not enough bytes in the buffer. + buffer.markReaderIndex(); + + // Read the length field. + final int length = buffer.readInt(); + + // Make sure if there's enough bytes in the buffer. + if (buffer.readableBytes() < length + 4) { + // The whole bytes were not received yet - return null. + // This method will be invoked again when more packets are + // received and appended to the buffer. + + // Reset to the marked position to read the length field again + // next time. + buffer.resetReaderIndex(); + return null; + } + + // Read the opcode. + final int opcode = buffer.readInt() & 0xff; + + // Create an executable packet using the buffer data. + return new Packet(opcode, buffer, length); + } + +} \ No newline at end of file diff --git a/rsc-server/src/org/openrsc/net/codec/PacketEncoder.java b/rsc-server/src/org/openrsc/net/codec/PacketEncoder.java new file mode 100644 index 0000000..ea8c026 --- /dev/null +++ b/rsc-server/src/org/openrsc/net/codec/PacketEncoder.java @@ -0,0 +1,36 @@ +package org.openrsc.net.codec; + +import org.jboss.netty.channel.Channel; +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.handler.codec.oneone.OneToOneEncoder; +import org.openrsc.net.packet.Packet; + +/** + * Writes an outgoing packet. + */ +public class PacketEncoder extends OneToOneEncoder { + + @Override + protected Object encode(ChannelHandlerContext ctx, Channel channel, Object obj) throws Exception { + // Get the packet object. + Packet packet = (Packet) obj; + + // Create a new buffer. + ChannelBuffer buffer = ChannelBuffers.dynamicBuffer(); + + // Write the packet length. + buffer.writeInt(packet.getPacketLength()); + + // Write the packet opcode. + buffer.writeInt(packet.getOpcode() & 0xff); + + // Write the packet data + buffer.writeBytes(packet.toByteArray()); + + // The buffer. + return buffer; + } + +} \ No newline at end of file diff --git a/rsc-server/src/org/openrsc/net/packet/Packet.java b/rsc-server/src/org/openrsc/net/packet/Packet.java new file mode 100644 index 0000000..12919d6 --- /dev/null +++ b/rsc-server/src/org/openrsc/net/packet/Packet.java @@ -0,0 +1,291 @@ +package org.openrsc.net.packet; + +import java.util.Arrays; +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; + +/** + * A dynamic object that contains data sent from/to client/server. + */ +public class Packet { + + private ChannelBuffer buffer = null; + private int opcode = 0; + + /** + * Creating a new empty packet. + * + * @param opcode + * the opcode of the packet + */ + public Packet(int opcode) { + // Creates a new big-endian dynamic buffer with the specified estimated data + // length. + // More accurate estimation yields less unexpected reallocation overhead. + this.buffer = ChannelBuffers.dynamicBuffer(4096/* 8192 */); + this.opcode = opcode; + } + + /** + * Creating an already filled packet, used at the handling of the incoming + * packets + * + * @param opcode + * the opcode of the packet + * @param buffer + * the buffer + * @param length + * the length of the packet + */ + public Packet(int opcode, ChannelBuffer buffer, int length) { + this.buffer = ChannelBuffers.copiedBuffer(buffer.readBytes(length)); + this.opcode = opcode; + } + + public int getOpcode() { + return opcode; + } + + /** + * The byte data type is an 8-bit signed two's complement integer. It has a + * minimum value of -128 and a maximum value of 127 (inclusive). + */ + public Packet putByte(int b) { + buffer.writeByte(b); + return this; + } + + public Packet putBytes(byte[] b) { + buffer.writeLong(b.length); + buffer.writeBytes(b); + return this; + } + + /** + * The short data type is a 16-bit signed two's complement integer. It has a + * minimum value of -32,768 and a maximum value of 32,767 (inclusive). + */ + public Packet putShort(short s) { + buffer.writeShort(s); + return this; + } + + /** + * Sends a small integer (a short). + */ + public Packet putSmallInt(int i) { + buffer.writeShort(i); + return this; + } + + /** + * The int data type is a 32-bit signed two's complement integer. It has a + * minimum value of -2,147,483,648 and a maximum value of 2,147,483,647 + * (inclusive). + */ + public Packet putInt(int i) { + buffer.writeInt(i); + return this; + } + + /** + * long: The long data type is a 64-bit signed two's complement integer. It has + * a minimum value of -9,223,372,036,854,775,808 and a maximum value of + * 9,223,372,036,854,775,807 (inclusive). Use this data type when you need a + * range of values wider than those provided by int. + */ + public Packet putLong(long l) { + buffer.writeLong(l); + return this; + } + + /** + * Encodes a String into a base37 integral value. + * + * @return The encoded String value. + */ + public Packet putBase37(String string) { + String s = ""; + for (int i = 0; i < string.length(); i++) { + char c = string.charAt(i); + if (c >= 'a' && c <= 'z') { + s = s + c; + } else if (c >= 'A' && c <= 'Z') { + s = s + (char) ((c + 97) - 65); + } else if (c >= '0' && c <= '9') { + s = s + c; + } else { + s = s + ' '; + } + } + + if (s.length() > 12) { + s = s.substring(0, 12); + } + long base = 0L; + for (int i = 0; i < s.length(); i++) { + char c1 = s.charAt(i); + base *= 37L; + if (c1 >= 'a' && c1 <= 'z') { + base += (1 + c1) - 97; + } else if (c1 >= '0' && c1 <= '9') { + base += (27 + c1) - 48; + } + } + buffer.writeLong(base); + return this; + } + + /** + * https://docs.oracle.com/javase/7/docs/api/java/lang/Double.html + */ + public Packet putDouble(double d) { + buffer.writeDouble(d); + return this; + } + + /** + * https://docs.oracle.com/javase/7/docs/api/java/lang/Float.html + */ + public Packet putFloat(float f) { + return putString(Float.toString(f)); + } + + public Packet putBoolean(boolean b) { + buffer.writeByte((byte) (b ? 1 : 0)); + return this; + } + + public Packet putString(String s) { + buffer.writeByte(s.getBytes().length); + buffer.writeBytes(s.getBytes()); + return this; + } + + /** + * @return Reads a Byte value from the buffer. + */ + public byte getByte() { + return buffer.readByte(); + } + + /** + * @return Reads an array of Byte values from the buffer. + */ + public byte[] getBytes() { + int length = (int) buffer.readLong(); + byte[] b = new byte[length]; + for (int i = 0; i < length; i++) { + b[i] = buffer.readByte(); + } + return b; + } + + /** + * @return Reads a Short from the buffer. + */ + public short getShort() { + return buffer.readShort(); + } + + /** + * @return Reads an Small Integer (Short) from the buffer. + */ + public int getSmallInt() { + return buffer.readShort(); + } + + /** + * @return Reads an Integer from the buffer. + */ + public int getInt() { + return buffer.readInt(); + } + + /** + * @return Reads a Long from the buffer. + */ + public long getLong() { + return buffer.readLong(); + } + + /** + * Decodes a base37 integral value into a String. + * + * @return The decoded String value. + */ + public String getBase37() { + long base = buffer.readLong(); + if (base < 0L) { + return "invalid_name"; + } + String string = ""; + while (base != 0L) { + int value = (int) (base % 37L); + base /= 37L; + if (value == 0) { + string = " " + string; + } else if (value < 27) { + if (base % 37L == 0L) { + string = (char) ((value + 65) - 1) + string; + } else { + string = (char) ((value + 97) - 1) + string; + } + } else { + string = (char) ((value + 48) - 27) + string; + } + } + return string; + } + + /** + * @return Reads a Double from the buffer. + */ + public double getDouble() { + return buffer.readDouble(); + } + + /** + * @return Reads a Float from the buffer. + */ + public float getFloat() { + return Float.parseFloat(getString()); + } + + /** + * @return Reads a Boolean from the buffer. + */ + public boolean getBoolean() { + return buffer.readByte() == 1; + } + + /** + * @return Reads a String from the buffer. + */ + public String getString() { + int length = getByte(); + byte[] b = new byte[length]; + for (int i = 0; i < b.length; i++) { + b[i] = getByte(); + } + return new String(b); + } + + /** + * @return The byte array contains all of the packet data. + */ + public byte[] toByteArray() { + if (buffer.hasArray()) { + return Arrays.copyOfRange(buffer.array(), 0, buffer.writerIndex()); + } + throw new IllegalStateException(); + } + + /** + * @return The packet length. + */ + public int getPacketLength() { + return buffer.readableBytes(); + } + +} \ No newline at end of file diff --git a/rsc-server/src/org/openrsc/net/packet/PacketHandler.java b/rsc-server/src/org/openrsc/net/packet/PacketHandler.java new file mode 100644 index 0000000..ceea754 --- /dev/null +++ b/rsc-server/src/org/openrsc/net/packet/PacketHandler.java @@ -0,0 +1,25 @@ +package org.openrsc.net.packet; + +import org.openrsc.model.player.Player; + +/** + */ +public interface PacketHandler { + + /** + * Executes the packet. + * + * @param player + * The player that sent the packet. + * @param packet + * The packet to execute. + */ + public void execute(Player player, Packet packet); + + /** + * @return True, if the packet execution should be queued until the next game + * tick. + */ + public boolean addToQueue(); + +} diff --git a/rsc-server/src/org/openrsc/net/packet/PacketManager.java b/rsc-server/src/org/openrsc/net/packet/PacketManager.java new file mode 100644 index 0000000..1f4aefa --- /dev/null +++ b/rsc-server/src/org/openrsc/net/packet/PacketManager.java @@ -0,0 +1,42 @@ +package org.openrsc.net.packet; + +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.openrsc.model.net.packet.*; +import org.openrsc.model.player.Player; + +/** + * The {@link PacketManager} class is the central hub for all packet based + * logic, this class contains purely static methods that will handle the setup + * and construction of each individual packet detected by the server. + */ +public class PacketManager { + + // An List containing all of the packets detected by the server. + private static Map packets = new HashMap<>(); + + /** + * Gets all of the packets that were detected by the server at launch. + * + * @return A List of {@link PacketHandler}'s + */ + public static PacketHandler get(int opcode) { + return packets.get(opcode); + } + + public static void loadPackets() { + // #0 = RSA Handshake + // #1 = Ping + // #2 = Login Request + packets.put(100, new SilentPacket()); + Logger.getLogger(PacketManager.class.getName()).log(Level.INFO, "Loaded " + packets.size() + " packets."); + } + + public static void execute(Player player, Packet packet) { + packets.get(packet.getOpcode()).execute(player, packet); + } + +} \ No newline at end of file diff --git a/rsc-server/src/org/openrsc/task/Task.java b/rsc-server/src/org/openrsc/task/Task.java new file mode 100644 index 0000000..7a8632c --- /dev/null +++ b/rsc-server/src/org/openrsc/task/Task.java @@ -0,0 +1,19 @@ +package org.openrsc.task; + +/** + * A task is a class which carries out a unit of work. + * + * @author Graham Edgecombe + */ +public interface Task { + + /** + * Executes the task. The general contract of the execute method is that it may + * take any action whatsoever. + * + * @param context + * The game engine this task is being executed in. + */ + public void execute(TaskEngine context); + +} diff --git a/rsc-server/src/org/openrsc/task/TaskEngine.java b/rsc-server/src/org/openrsc/task/TaskEngine.java new file mode 100644 index 0000000..afd0d4c --- /dev/null +++ b/rsc-server/src/org/openrsc/task/TaskEngine.java @@ -0,0 +1,192 @@ +package org.openrsc.task; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.openrsc.Config; +import org.openrsc.net.Server; +import org.openrsc.task.util.BlockingExecutorService; + +/** + * Processes all the scheduled tasks in one single scheduled + * ExecutorService. This service is scheduled which means + * Events are also submitted to it. + */ +public class TaskEngine implements Runnable { + + /** + * A queue of pending tasks. + */ + private final BlockingQueue tasks = new LinkedBlockingQueue(); + + /** + * The scheduled service. + */ + private final ScheduledExecutorService scheduledService = Executors.newScheduledThreadPool(1); + + /** + * The task service, used by ParallelTasks. + */ + private final BlockingExecutorService taskService; + + /** + * Running flag. + */ + private boolean running = false; + + /** + * Thread instance. + */ + private Thread thread; + + @SuppressWarnings("unused") + public TaskEngine() { + this.taskService = Config.TASK_ENGINE_THREAD_COUNT == 1 + ? new BlockingExecutorService(Executors.newSingleThreadExecutor()) + : new BlockingExecutorService(Executors.newFixedThreadPool(Config.TASK_ENGINE_THREAD_COUNT)); + } + + /** + * Submits a new task which is processed on the scheduled thread as soon as + * possible. + * + * @param task + * The task to submit. + */ + public void pushTask(Task task) { + tasks.offer(task); + } + + /** + * Starts the thread. + */ + public void start() { + if (running) { + throw new IllegalStateException(getClass().getName() + " already running.."); + } + running = true; + thread = new Thread(this); + thread.start(); + } + + /** + * Stops the Engine's thread. + */ + public void stop() { + if (!running) { + throw new IllegalStateException(getClass().getName() + " already stopped.."); + } + running = false; + thread.interrupt(); + } + + @Override + public void run() { + try { + while (running) { + try { + final Task task = tasks.take(); + submitLogic(new Runnable() { + @Override + public void run() { + task.execute(TaskEngine.this); + } + }); + } catch (InterruptedException e) { + continue; + } + } + } finally { + scheduledService.shutdown(); + taskService.shutdown(); + } + } + + /** + * Schedules a task to run in the scheduled service. + * + * @param runnable + * The runnable. + * @param delay + * The delay. + * @param unit + * The time unit. + * @return The ScheduledFuture of the scheduled logic. + */ + public ScheduledFuture scheduleLogic(final Runnable runnable, long delay, TimeUnit unit) { + return scheduledService.schedule(new Runnable() { + public void run() { + try { + runnable.run(); + } catch (Throwable t) { + handleError(t); + } + } + }, delay, unit); + } + + /** + * Submits a task to run in the parallel task service. + * + * @param runnable + * The runnable. + */ + public void submitTask(final Runnable runnable) { + taskService.submit(new Runnable() { + public void run() { + try { + runnable.run(); + } catch (Throwable t) { + handleError(t); + } + } + }); + } + + /** + * Submits a task to run in the scheduled service. + * + * @param runnable + * The runnable. + */ + public void submitLogic(final Runnable runnable) { + scheduledService.submit(new Runnable() { + public void run() { + try { + runnable.run(); + } catch (Throwable t) { + handleError(t); + } + } + }); + } + + /** + * Waits for pending parallel tasks. + * + * @throws ExecutionException + * If an error occurred during a task. + */ + public void waitForPendingParallelTasks() throws ExecutionException { + taskService.waitForPendingTasks(); + } + + /** + * Handles an exception in any of the pools. + * + * @param t + * The exception. + */ + private void handleError(Throwable t) { + Logger.getLogger(getClass().getName()).log(Level.SEVERE, "An error has occured in an executor service.", t); + Server.getInstance().shutdown(true); + } + +} diff --git a/rsc-server/src/org/openrsc/task/impl/ConsecutiveTask.java b/rsc-server/src/org/openrsc/task/impl/ConsecutiveTask.java new file mode 100644 index 0000000..224181f --- /dev/null +++ b/rsc-server/src/org/openrsc/task/impl/ConsecutiveTask.java @@ -0,0 +1,44 @@ +package org.openrsc.task.impl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.openrsc.task.Task; +import org.openrsc.task.TaskEngine; + +/** + * A task which executes a group of tasks in a guaranteed sequence. + * + * @author Graham Edgecombe + */ +public class ConsecutiveTask implements Task { + + /** + * The tasks. + */ + private Collection tasks; + + /** + * Creates the consecutive task. + * + * @param tasks + * The child tasks to execute. + */ + public ConsecutiveTask(Task... tasks) { + List taskList = new ArrayList(); + for (Task task : tasks) { + taskList.add(task); + } + this.tasks = Collections.unmodifiableCollection(taskList); + } + + @Override + public void execute(TaskEngine context) { + for (Task task : tasks) { + task.execute(context); + } + } + +} diff --git a/rsc-server/src/org/openrsc/task/impl/ParallelTask.java b/rsc-server/src/org/openrsc/task/impl/ParallelTask.java new file mode 100644 index 0000000..7963d67 --- /dev/null +++ b/rsc-server/src/org/openrsc/task/impl/ParallelTask.java @@ -0,0 +1,55 @@ +package org.openrsc.task.impl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; + +import org.openrsc.task.Task; +import org.openrsc.task.TaskEngine; + +/** + * A task which can execute multiple child tasks simultaneously. + * + * @author Graham Edgecombe + */ +public class ParallelTask implements Task { + + /** + * The child tasks. + */ + private Collection tasks; + + /** + * Creates the parallel task. + * + * @param tasks + * The child tasks. + */ + public ParallelTask(Task... tasks) { + List taskList = new ArrayList(); + for (Task task : tasks) { + taskList.add(task); + } + this.tasks = Collections.unmodifiableCollection(taskList); + } + + @Override + public void execute(final TaskEngine context) { + for (final Task task : tasks) { + context.submitTask(new Runnable() { + @Override + public void run() { + task.execute(context); + } + }); + } + try { + context.waitForPendingParallelTasks(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/rsc-server/src/org/openrsc/task/util/BlockingExecutorService.java b/rsc-server/src/org/openrsc/task/util/BlockingExecutorService.java new file mode 100644 index 0000000..8845f5b --- /dev/null +++ b/rsc-server/src/org/openrsc/task/util/BlockingExecutorService.java @@ -0,0 +1,149 @@ +package org.openrsc.task.util; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * An ExecutorService that waits for all its events to finish + * executing. + */ +public class BlockingExecutorService implements ExecutorService { + + /** + * The service backing this service. + */ + private ExecutorService service; + + /** + * A list of pending tasks. + */ + private BlockingQueue> pendingTasks = new LinkedBlockingQueue>(); + + /** + * Creates the executor service. + * + * @param service + * The service backing this service. + */ + public BlockingExecutorService(ExecutorService service) { + this.service = service; + } + + /** + * Waits for pending tasks to complete. + * + * @throws ExecutionException + * if an error in a task occurred. + */ + public void waitForPendingTasks() throws ExecutionException { + while (pendingTasks.size() > 0) { + if (isShutdown()) { + return; + } + try { + pendingTasks.take().get(); + } catch (InterruptedException e) { + continue; + } + } + } + + /** + * Gets the number of pending tasks. + * + * @return The number of pending tasks. + */ + public int getPendingTaskAmount() { + return pendingTasks.size(); + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + return service.awaitTermination(timeout, unit); + } + + @Override + public List> invokeAll(Collection> tasks) throws InterruptedException { + List> futures = service.invokeAll(tasks); + for (Future future : futures) { + pendingTasks.add(future); + } + return futures; + } + + @Override + public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException { + List> futures = service.invokeAll(tasks, timeout, unit); + for (Future future : futures) { + pendingTasks.add(future); + } + return futures; + } + + @Override + public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { + return service.invokeAny(tasks); + } + + @Override + public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return service.invokeAny(tasks, timeout, unit); + } + + @Override + public boolean isShutdown() { + return service.isShutdown(); + } + + @Override + public boolean isTerminated() { + return service.isTerminated(); + } + + @Override + public void shutdown() { + service.shutdown(); + } + + @Override + public List shutdownNow() { + return service.shutdownNow(); + } + + @Override + public Future submit(Callable task) { + Future future = service.submit(task); + pendingTasks.add(future); + return future; + } + + @Override + public Future submit(Runnable task) { + Future future = service.submit(task); + pendingTasks.add(future); + return future; + } + + @Override + public Future submit(Runnable task, T result) { + Future future = service.submit(task, result); + pendingTasks.add(future); + return future; + } + + @Override + public void execute(Runnable command) { + service.execute(command); + } + +} diff --git a/rsc-server/src/org/openrsc/util/GameUtils.java b/rsc-server/src/org/openrsc/util/GameUtils.java new file mode 100644 index 0000000..b4de7ca --- /dev/null +++ b/rsc-server/src/org/openrsc/util/GameUtils.java @@ -0,0 +1,133 @@ +package org.openrsc.util; + +import java.io.File; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Calendar; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +public class GameUtils { + + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.0"); + + private static final char VALID_CHARS[] = { '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '_', '=', '+', + '[', '{', ']', '}', ';', ':', '\'', '"', ',', '<', '.', '>', '/', '?', ' ', '|', '`', '~', '\\' }; + + public GameUtils() { + // .. + } + + /** + * @return The current calendar date, formatted as Month ##, #### + */ + public static String getCalendarDate() { + return Calendar.getInstance().getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault()) + " " + + Calendar.getInstance().get(Calendar.DAY_OF_MONTH) + ", " + Calendar.getInstance().get(Calendar.YEAR); + } + + /** + * @return The current calendar time, formatted as HH:MM.SS + */ + public static String getCalendarTime() { + return Calendar.getInstance().get(Calendar.HOUR) + ":" + Calendar.getInstance().get(Calendar.MINUTE) + "." + + (Calendar.getInstance().get(Calendar.SECOND) + 1); + } + + /** + * @return The current time in milliseconds (using nanosecond precision). + */ + public static long getCurrentTimeMillis() { + return TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); + } + + /** + * Request a garbage collection, to free unallocated memory. + */ + public static String gc() { + long usage = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); + Runtime.getRuntime().runFinalization(); + Runtime.getRuntime().gc(); + long cleanup = usage - (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()); + if (cleanup > 0) { + return formatSize(cleanup); + } + return "0"; + } + + /** + * aA-zZ, 0-9, and VALID_CHARS[] are THE ONLY usable chat characters. + */ + public static boolean isValidCharacter(char character) { + if (Character.isLetterOrDigit(character)) { + return true; + } + for (int i = 0; i < VALID_CHARS.length; i++) { + if (character == VALID_CHARS[i]) { + return true; + } + } + return false; + } + + /** + * Replaces the input string with asterisks. + */ + public static String replaceWithAsterisks(String string) { + StringBuilder asterisks = new StringBuilder(); + int l = string.length(); + for (int i = 0; i < l; i++) { + asterisks.append("*"); + } + return asterisks.toString(); + } + + /** + * Formats the given number with a more readable output, example: 9001 becomes + * 9,001. + */ + public static String formatNumber(int number) { + return NumberFormat.getInstance().format(number); + } + + /** + * @param millis + * The millisecond time to be formatted. + * @return Returns the given millisecond time value as HH:MM:SS + */ + public static String formatTimeMillis(long millis) { + if (millis < 1000) { + return millis + "ms"; + } + int s = (int) (millis / 1000) % 60; + int m = (int) ((millis / (1000 * 60)) % 60); + int h = (int) ((millis / (1000 * 60 * 60)) % 24); + return h + ":" + m + ":" + s; + } + + /** + * Formats the supplied size value from bytes to a more readable value. + */ + public static String formatSize(long bytes) { + double kb = bytes / 1024.0; + double mb = (kb / 1024.0); + double gb = (mb / 1024.0); + if (gb > 1) { + return DECIMAL_FORMAT.format(gb).concat("GB"); + } else if (mb > 1) { + return DECIMAL_FORMAT.format(mb).concat("MB"); + } else if (kb > 1) { + return DECIMAL_FORMAT.format(kb).concat("KB"); + } else { + return DECIMAL_FORMAT.format(bytes).concat("B"); + } + } + + /** + * @see #formatSize(long) + */ + public static String formatSize(File file) { + return formatSize(file.length()); + } + +} \ No newline at end of file diff --git a/rsc-server/src/server/RuneServer.java b/rsc-server/src/server/RuneServer.java deleted file mode 100644 index 566f0a0..0000000 --- a/rsc-server/src/server/RuneServer.java +++ /dev/null @@ -1,153 +0,0 @@ -package server; - -import java.io.IOException; -import java.net.ServerSocket; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import server.entityhandling.defs.DoorDef; -import server.entityhandling.defs.GameObjectDef; -import server.entityhandling.defs.ItemDef; -import server.entityhandling.defs.NpcDef; -import server.entityhandling.defs.PrayerDef; -import server.entityhandling.defs.SpellDef; -import server.entityhandling.defs.TileDef; -import server.game.world.World; -import server.game.world.WorldLoader; -import server.net.AcceptThread; -import server.net.Client; -import server.net.SendPacketTask; -import server.net.SocketUtils; -import server.packets.Packet; -import server.packets.builders.LoginSuccessPacketBuilder; -import server.packets.from_server.KickPacket; -import server.res.ResourceLoader; -import server.res.Resources; - -public class RuneServer { - - public static final int SERVER_PORT = 7780; - - // Kick reasons - public static final byte SKIP_REASON = 0; - public static final byte SERVER_CLOSED = 1; - - /** - * Time to wait for all clients to get kicked before shutting down. - */ - private static final long SHUTDOWN_TIME = 0; - - private ExecutorService executor; - - private ServerSocket serverSocket; - - private boolean exiting; - private volatile boolean dead; - - private List clients = new ArrayList<>(); - - public RuneServer() throws IOException { - serverSocket = new ServerSocket(SERVER_PORT); - executor = Executors.newCachedThreadPool(); - } - - public void load() { - - // Load resources - Resources.doors = (DoorDef[]) ResourceLoader.loadGzipData("defs/DoorDef.xml.gz"); - Resources.gameObjects = (GameObjectDef[]) ResourceLoader.loadGzipData("defs/GameObjectDef.xml.gz"); - Resources.items = (ItemDef[]) ResourceLoader.loadGzipData("defs/ItemDef.xml.gz"); - Resources.npcs = (NpcDef[]) ResourceLoader.loadGzipData("defs/NPCDef.xml.gz"); - Resources.prayers = (PrayerDef[]) ResourceLoader.loadGzipData("defs/PrayerDef.xml.gz"); - Resources.spells = (SpellDef[]) ResourceLoader.loadGzipData("defs/SpellDef.xml.gz"); - Resources.tiles = (TileDef[]) ResourceLoader.loadGzipData("defs/TileDef.xml.gz"); - - // Load world - World world = new World(); - WorldLoader worldLoader = new WorldLoader(); - worldLoader.loadWorld(world); - } - - public void requestExit() { - exiting = true; - } - - public boolean isExiting() { - return exiting; - } - - public void kill() { - - // Ensure this method is only called once - if (dead) { - return; - } - - dead = true; - exiting = true; - - // Stop accepting new clients - SocketUtils.close(serverSocket); - - // Kick all clients - for (Client client : clients) { - Packet kickPacket = new KickPacket(SERVER_CLOSED); - executor.execute(new SendPacketTask(client, kickPacket)); - } - - // Wait for clients to get kicked - executor.shutdown(); - try { - executor.awaitTermination(SHUTDOWN_TIME, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - // Kill all client connections - for (Client client : clients) { - client.kill(); - } - } - - public void addClient(Client client) { - synchronized (clients) { - System.out.println("Client connected from " + client.getAddress()); - Packet packet = new LoginSuccessPacketBuilder().build(); - executor.execute(new SendPacketTask(client, packet)); - clients.add(client); - } - } - - public void run() throws IOException { - - // Listen for new clients in a separate thread - AcceptThread acceptThread = new AcceptThread(this, serverSocket); - executor.execute(acceptThread); - - // Server main thread - while (!exiting) { - - removeDeadClients(); - - try { - Thread.sleep(10); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - - private void removeDeadClients() { - synchronized (clients) { - clients = clients - .stream() - .filter(c -> !c.isDead()) - .collect(Collectors.toList()); - } - } - -} diff --git a/rsc-server/src/server/game/model/Mob.java b/rsc-server/src/server/game/model/Mob.java deleted file mode 100644 index 494838b..0000000 --- a/rsc-server/src/server/game/model/Mob.java +++ /dev/null @@ -1,41 +0,0 @@ -package server.game.model; - -public class Mob { - - public int admin; - public long nameLong; - public String name; - public int serverIndex; - public int mobIntUnknown; - public int x; - public int z; - public int type; - public int stepCount; - public int currentSprite; - public int nextAnimation; - public int movingStep; - public int waypointCurrent; - public int waypointsX[] = new int[10]; - public int waypointsZ[] = new int[10]; - public int animationCount[] = new int[12]; - public String lastMessage; - public int lastMessageTimeout; - public int anInt162; - public int anInt163; - public int damageTaken; - public int healthCurrent; - public int healthMax; - public int combatTimer; - public int level = -1; - public int colourHairType; - public int colourTopType; - public int colourBottomType; - public int colourSkinType; - public int attackingCameraInt; - public int attackingMobIndex; - public int attackingNpcIndex; - public int projectileRange; - public boolean unusedBool; - public int unusedInt = -1; - public int anInt179; -} diff --git a/rsc-server/src/server/game/world/World.java b/rsc-server/src/server/game/world/World.java deleted file mode 100644 index 41c830a..0000000 --- a/rsc-server/src/server/game/world/World.java +++ /dev/null @@ -1,12 +0,0 @@ -package server.game.world; - -import server.game.model.Sector; - -public class World { - - private static final int SECTORS_X = 1000; - private static final int SECTORS_Z = 1000; - - private Sector[][] sectors = new Sector[SECTORS_X][SECTORS_Z]; - -} diff --git a/rsc-server/src/server/game/world/WorldLoader.java b/rsc-server/src/server/game/world/WorldLoader.java deleted file mode 100644 index 9c2e23c..0000000 --- a/rsc-server/src/server/game/world/WorldLoader.java +++ /dev/null @@ -1,120 +0,0 @@ -package server.game.world; - -import java.util.List; -import java.util.zip.ZipFile; - -import server.entityhandling.locs.GameObjectLoc; -import server.entityhandling.locs.ItemLoc; -import server.entityhandling.locs.NpcLoc; -import server.res.ResourceLoader; - -public class WorldLoader { - - @SuppressWarnings("unchecked") - public void loadWorld(World world) { - - ZipFile tileArchive = ResourceLoader.loadZipData("Landscape.tar.gz"); - - for (int lvl = 0; lvl < 4; lvl++) { - int wildX = 2304; - int wildY = 1776 - (lvl * 944); - for (int sx = 0; sx < 1000; sx += 48) { - for (int sy = 0; sy < 1000; sy += 48) { - int x = (sx + wildX) / 48; - int y = (sy + (lvl * 944) + wildY) / 48; - loadSection(x, y, lvl, world, sx, sy + (944 * lvl)); - } - } - } - - List gameObjectLocs = (List) - ResourceLoader.loadGzipData("locs/GameObjectLoc.xml.gz"); - List itemLocs = (List) - ResourceLoader.loadGzipData("locs/ItemLoc.xml.gz"); - List npcLocs = (List) - ResourceLoader.loadGzipData("locs/NpcLoc.xml.gz"); - - for (GameObjectLoc gameObject : gameObjectLocs) { -// world.registerGameObject(new GameObject(gameObject)); - } - for (ItemLoc item : itemLocs) { -// world.registerItem(new Item(item)); - } - for (NpcLoc npc : npcLocs) { -// world.registerNpc(new Npc(npc)); - } - } - - private void loadSection(int sectionX, int sectionY, int height, - World world, int bigX, int bigY) { -// Sector s = null; -// try { -// String filename = "h" + height + "x" + sectionX + "y" + sectionY; -// ZipEntry e = tileArchive.getEntry(filename); -// if (e == null) { -// throw new Exception("Missing tile: " + filename); -// } -// ByteBuffer data = DataConversions.streamToBuffer( -// new BufferedInputStream(tileArchive.getInputStream(e))); -// s = Sector.unpack(data); -// // s = modifyAndSave(filename, s, bigX, bigY); -// } catch (Exception e) { -// e.printStackTrace(); -// } -// -// for (int y = 0; y < Sector.DEPTH; y++) { -// for (int x = 0; x < Sector.WIDTH; x++) { -// int bx = bigX + x; -// int by = bigY + y; -// if (!world.withinWorld(bx, by)) { -// continue; -// } -// if ((s.getTile(x, y).groundOverlay & 0xff) == 250) { -// s.getTile(x, y).groundOverlay = (byte) 2; -// } -// int groundOverlay = s.getTile(x, y).groundOverlay & 0xFF; -// if (groundOverlay > 0 && Resources -// .getTileDef(groundOverlay - 1).getObjectType() != 0) { -// world.getTileValue(bx, by).mapValue |= 0x40; // 64 -// } -// -// int verticalWall = s.getTile(x, y).verticalWall & 0xFF; -// if (verticalWall > 0 -// && Resources.getDoorDef(verticalWall - 1) -// .getUnknown() == 0 -// && Resources.getDoorDef(verticalWall - 1) -// .getDoorType() != 0) { -// world.getTileValue(bx, by).mapValue |= 1; // 1 -// world.getTileValue(bx, by - 1).mapValue |= 4; // 4 -// } -// -// int horizontalWall = s.getTile(x, y).horizontalWall & 0xFF; -// if (horizontalWall > 0 -// && Resources.getDoorDef(horizontalWall - 1) -// .getUnknown() == 0 -// && Resources.getDoorDef(horizontalWall - 1) -// .getDoorType() != 0) { -// world.getTileValue(bx, by).mapValue |= 2; // 2 -// world.getTileValue(bx - 1, by).mapValue |= 8; // 8 -// } -// -// int diagonalWalls = s.getTile(x, y).diagonalWalls; -// if (diagonalWalls > 0 && diagonalWalls < 12000 -// && Resources.getDoorDef(diagonalWalls - 1) -// .getUnknown() == 0 -// && Resources.getDoorDef(diagonalWalls - 1) -// .getDoorType() != 0) { -// world.getTileValue(bx, by).mapValue |= 0x20; // 32 -// } -// if (diagonalWalls > 12000 && diagonalWalls < 24000 -// && Resources.getDoorDef(diagonalWalls - 12001) -// .getUnknown() == 0 -// && Resources.getDoorDef(diagonalWalls - 12001) -// .getDoorType() != 0) { -// world.getTileValue(bx, by).mapValue |= 0x10; // 16 -// } -// } -// } - } - -} diff --git a/rsc-server/src/server/main/RuneServerLauncher.java b/rsc-server/src/server/main/RuneServerLauncher.java deleted file mode 100644 index fb5d791..0000000 --- a/rsc-server/src/server/main/RuneServerLauncher.java +++ /dev/null @@ -1,28 +0,0 @@ -package server.main; - -import server.RuneServer; - -public class RuneServerLauncher { - - public static void main(String[] args) { - - RuneServer server = null; - - try { - - server = new RuneServer(); - server.load(); - server.run(); - - } catch (Exception e) { - - // Exit cleanly if an error occurs - if (server != null) { - server.kill(); - } - - e.printStackTrace(); - } - } - -} diff --git a/rsc-server/src/server/net/AcceptThread.java b/rsc-server/src/server/net/AcceptThread.java deleted file mode 100644 index e6d30b8..0000000 --- a/rsc-server/src/server/net/AcceptThread.java +++ /dev/null @@ -1,58 +0,0 @@ -package server.net; - -import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; - -import server.RuneServer; - -public class AcceptThread implements Runnable { - - private RuneServer server; - - private ServerSocket serverSocket; - - public AcceptThread(RuneServer server, ServerSocket serverSocket) - throws IOException { - this.server = server; - this.serverSocket = serverSocket; - } - - @Override - public void run() { - - System.out.println( - "Server listening on port " + RuneServer.SERVER_PORT); - - while (!server.isExiting()) { - - Socket clientSocket = null; - Client client = null; - - // Listen for a new Client - try { - clientSocket = serverSocket.accept(); - client = new Client(clientSocket); - - } catch (IOException e) { - - e.printStackTrace(); - - // Close the new Socket if an error occurs - SocketUtils.close(clientSocket); - - // Kill the server if the socket has closed - if (serverSocket.isClosed()) { - server.kill(); - break; - } - } - - // Add the new client - if (client != null) { - server.addClient(client); - } - } - } - -} diff --git a/rsc-server/src/server/net/Client.java b/rsc-server/src/server/net/Client.java deleted file mode 100644 index b3f3c36..0000000 --- a/rsc-server/src/server/net/Client.java +++ /dev/null @@ -1,44 +0,0 @@ -package server.net; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.net.Socket; - -import server.packets.Packet; -import server.packets.PacketSerializer; -import server.packets.PacketSerializers; - -public class Client { - - private Socket socket; - private DataInputStream in; - private DataOutputStream out; - - public Client(Socket socket) throws IOException { - this.socket = socket; - - in = new DataInputStream(socket.getInputStream()); - out = new DataOutputStream(socket.getOutputStream()); - } - - public void kill() { - SocketUtils.close(socket); - } - - public boolean isDead() { - return socket.isClosed(); - } - - public String getAddress() { - return socket.getInetAddress().getHostAddress(); - } - - public void send(Packet packet) throws IOException { - PacketSerializer packetSerializer = PacketSerializers.get(packet.id); - if (packetSerializer != null) { - packetSerializer.serialize(out, packet); - } - } - -} diff --git a/rsc-server/src/server/net/SendPacketTask.java b/rsc-server/src/server/net/SendPacketTask.java deleted file mode 100644 index 72a24ff..0000000 --- a/rsc-server/src/server/net/SendPacketTask.java +++ /dev/null @@ -1,31 +0,0 @@ -package server.net; - -import java.io.IOException; - -import server.packets.Packet; - -public class SendPacketTask implements Runnable { - - private Client client; - private Packet packet; - - public SendPacketTask(Client client, Packet packet) { - this.client = client; - this.packet = packet; - } - - @Override - public void run() { - try { - - if (!client.isDead()) { - client.send(packet); - } - - } catch (IOException e) { - e.printStackTrace(); - client.kill(); - } - } - -} diff --git a/rsc-server/src/server/net/SocketUtils.java b/rsc-server/src/server/net/SocketUtils.java deleted file mode 100644 index a1dd157..0000000 --- a/rsc-server/src/server/net/SocketUtils.java +++ /dev/null @@ -1,34 +0,0 @@ -package server.net; - -import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; - -public class SocketUtils { - - /** - * Closes a Socket safely. - * - * @param socket - */ - public static void close(Socket socket) { - if (socket != null && !socket.isClosed()) { - try { - socket.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - public static void close(ServerSocket serverSocket) { - if (serverSocket != null && !serverSocket.isClosed()) { - try { - serverSocket.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - -} diff --git a/rsc-server/src/server/packets/Packet.java b/rsc-server/src/server/packets/Packet.java deleted file mode 100644 index f86441b..0000000 --- a/rsc-server/src/server/packets/Packet.java +++ /dev/null @@ -1,15 +0,0 @@ -package server.packets; - -public abstract class Packet { - - // Server Packet IDs - public static final short LOGIN_SUCCESS = 0; - public static final short KICK = 1; - - public final short id; - - public Packet(short id) { - this.id = id; - } - -} diff --git a/rsc-server/src/server/packets/PacketSerializer.java b/rsc-server/src/server/packets/PacketSerializer.java deleted file mode 100644 index 3ec4dc4..0000000 --- a/rsc-server/src/server/packets/PacketSerializer.java +++ /dev/null @@ -1,11 +0,0 @@ -package server.packets; - -import java.io.DataOutputStream; -import java.io.IOException; - -public abstract class PacketSerializer { - - public abstract void serialize( - DataOutputStream out, Packet packetBeforeCast) throws IOException; - -} diff --git a/rsc-server/src/server/packets/PacketSerializers.java b/rsc-server/src/server/packets/PacketSerializers.java deleted file mode 100644 index 8bdc203..0000000 --- a/rsc-server/src/server/packets/PacketSerializers.java +++ /dev/null @@ -1,25 +0,0 @@ -package server.packets; - -import java.util.HashMap; -import java.util.Map; - -import server.packets.serializers.LoginSuccessPacketSerializer; - -public class PacketSerializers { - - private static Map registeredSerializers = - new HashMap<>(); - - static { - register(Packet.LOGIN_SUCCESS, new LoginSuccessPacketSerializer()); - } - - private static void register(short id, PacketSerializer serializer) { - registeredSerializers.put(id, serializer); - } - - public static PacketSerializer get(short id) { - return registeredSerializers.get(id); - } - -} diff --git a/rsc-server/src/server/packets/builders/LoginSuccessPacketBuilder.java b/rsc-server/src/server/packets/builders/LoginSuccessPacketBuilder.java deleted file mode 100644 index f3f76c2..0000000 --- a/rsc-server/src/server/packets/builders/LoginSuccessPacketBuilder.java +++ /dev/null @@ -1,13 +0,0 @@ -package server.packets.builders; - -import server.packets.Packet; -import server.packets.from_server.LoginSuccessPacket; - -public class LoginSuccessPacketBuilder extends PacketBuilder { - - @Override - public Packet build() { - return new LoginSuccessPacket(); - } - -} diff --git a/rsc-server/src/server/packets/builders/PacketBuilder.java b/rsc-server/src/server/packets/builders/PacketBuilder.java deleted file mode 100644 index bde7ffb..0000000 --- a/rsc-server/src/server/packets/builders/PacketBuilder.java +++ /dev/null @@ -1,9 +0,0 @@ -package server.packets.builders; - -import server.packets.Packet; - -public abstract class PacketBuilder { - - public abstract Packet build(); - -} diff --git a/rsc-server/src/server/packets/from_server/KickPacket.java b/rsc-server/src/server/packets/from_server/KickPacket.java deleted file mode 100644 index 92dfdf4..0000000 --- a/rsc-server/src/server/packets/from_server/KickPacket.java +++ /dev/null @@ -1,15 +0,0 @@ -package server.packets.from_server; - -import server.packets.Packet; - -public class KickPacket extends Packet { - - public final byte reason; - - public KickPacket(byte reason) { - super(Packet.KICK); - - this.reason = reason; - } - -} diff --git a/rsc-server/src/server/packets/from_server/LoginSuccessPacket.java b/rsc-server/src/server/packets/from_server/LoginSuccessPacket.java deleted file mode 100644 index 2b45244..0000000 --- a/rsc-server/src/server/packets/from_server/LoginSuccessPacket.java +++ /dev/null @@ -1,11 +0,0 @@ -package server.packets.from_server; - -import server.packets.Packet; - -public class LoginSuccessPacket extends Packet { - - public LoginSuccessPacket() { - super(Packet.LOGIN_SUCCESS); - } - -} diff --git a/rsc-server/src/server/packets/serializers/LoginSuccessPacketSerializer.java b/rsc-server/src/server/packets/serializers/LoginSuccessPacketSerializer.java deleted file mode 100644 index 84ea65d..0000000 --- a/rsc-server/src/server/packets/serializers/LoginSuccessPacketSerializer.java +++ /dev/null @@ -1,19 +0,0 @@ -package server.packets.serializers; - -import java.io.DataOutputStream; -import java.io.IOException; - -import server.packets.Packet; -import server.packets.PacketSerializer; -import server.packets.from_server.LoginSuccessPacket; - -public class LoginSuccessPacketSerializer extends PacketSerializer { - - @Override - public void serialize(DataOutputStream out, Packet packetBeforeCast) - throws IOException { - LoginSuccessPacket p = (LoginSuccessPacket) packetBeforeCast; - out.writeShort(p.id); - } - -}