Renoria - Java MMORPG

Renoria is a 2D-Sidescroller MMORPG written in Java based on LWJGL. It has been in development since 2008 but I put development on hold from 2010-2013 and I’ve started on the development of it again.

This has been my first project in Java and I started writing it when I was 14 (I’m 19 now).

FACEBOOK: https://www.facebook.com/RenoriaOfficial

INDIEDB: http://www.indiedb.com/games/renoria

Technical details (client):

  • Renderer: Java2D / LWJGL / Android GLSurfaceView
  • Audio: JavaSound
  • Networking: Java NIO
  • Engine: Ux2Engine

(Game engine is extremely optimised, runs 10K+ objects at 60fps.)

Technical details (server):

  • Architecture: distributed
  • Networking: Java NIO
  • Technology: Java Server

[b]NOTE: WE ARE CURRENTLY ACCEPTING ALPHA TESTING REGISTRATIONS AT: http://ux-soft.com/renoria/[b]

FEATURES:

  • Create your own story! Do whatever you want!
  • Fight monsters, level up!
  • Mine rocks, forge your own weapons and armors!
  • Shear sheep, spin thread, create your own clothes!
  • Got a gold bar, hammer and jewel? Create your own jewelry!-


SUPPORTED PLATFORMS:

[li]Windows

  • Mac OSX
  • Linux, *Nix
  • Android Tablets - Soon to come
  • iPad - Soon to come

Screenshots:

Looks great, I am thinking about adding network/multi play to my game, is it best to consider this form the outset?

Also what are you next features going to be?

Thank you!

Yep. You need to consider this from the start, because there are many features or additions that you might add that won’t work well in a synchronized network environment (think latency, network delays, synchronization etc.)

My next features will be addition of skills, buffs, monster powerups, character emotes, and other nice little effects in the game (think underwater lens, heat haze, etc). Also the programming of boss monsters that have special attacks and special features, such as spawning minions, special attacks and special rewards from killing bosses, oh and lastly I’m going to build a quest scripting system and cinematic scripting system (for cutscenes).

I’m also rewriting the main part of the engine for easy portability to other languages, for example Objective-C and C++ so the game can easily be moved onto iPads and other devices.

And I already kinda have a heat haze effect going:

UPDATE:

Rewrote main rendering part of graphics engine. Now handling 10K monsters on the map at a time at a nice 60-80 FPS (with debug data enabled):

Running on Java2D, JRE6

Also prettier character editing tool:

UPDATE:

  • Characters now have face, hair, and can wear equipments
  • Ranged attacks (arrows & throwing stars) are now coded and work. They’re also perfectly synchronized between clients.

Throwing stars:

Hi

I’d like to give it a try as soon as possible. Keep up the good work ;D

Your art looks really good, but I don’t think that purple text fits just quite right! :stuck_out_tongue:

Since you are doing it in Java2D, you can give the text a 3D look very easily. Just draw the text twice, once in black and then in white with some small offset, and it looks so cool. Also nice game. I want to see the code of engine, Can I? I also would like to test the game.

I would love to take part in the alpha!

Thank you, I’ll definitely sign you up for Alpha!

Haha, yeah it’s just there as a debug console :stuck_out_tongue:
I’m using it to debug messages from the server, you can see messages like Vertical Difference (which is a method I use to test for hackers that do things like Fly hacking or jumping through platforms that they shouldn’t be able to.)

Yep, I’ll probably end up doing that eventually. I can post parts of the engine source upon request, but I won’t be posting the entire source to the client or server(s) (for obvious reasons) :slight_smile:
Thank you! I’ll sign you up for Alpha when its ready!

Will definitely sign you up to Alpha too!

UPDATE:

  • Close range melee attacks are now handled by remote clients instead of being frame-by-frame transmitted by the server. Has the advantage of the illusion of decreased latency, and better timing & accuracy
  • Ranged attacks are now handled via a timer on the client instead of being run on the server. Same advantage applies above. MirrorRNG is used to display instantaneous damage instead of waiting for a damage number from the server. Basically the client and server carry a RNG with the same seed (sent from the server upon login) and this is used to randomise damage, and it also ensures both numbers generated by the server and client are in sync.
  • Rewote the MirrorRNG to use MersenneTwister instead of java.util.Random - would ease porting to other platforms later on.
  • Rewrote the packet encryption scheme to use BouncyCastle instead of JCE.
  • Clients will now only use the last 10 movement reference frames for object extrapolation, discarding the rest.

Screenshot of update:

Source code for rendering characters if interested:


    // providers
    private CharacterPartsProvider skinProvider = null;
    private EnumMap<EquipSlot, CharacterPartsProvider> equipsProvider = new EnumMap(EquipSlot.class);
    private CharacterPartsProvider hairProvider = null;
    private CharacterPartsProvider faceProvider = null;
    // rendering stuff
    private Set<CharacterPart> currentParts = new TreeSet<CharacterPart>(CHAR_PART_Z_COMPARATOR);
    private CharacterStance lastRenderedStance = null;
    private int lastFrame = -1;
    private boolean partsDirty = false;
    
    ///////////////////////////////////////////////////////////////////////////
    static final Comparator<CharacterPart> CHAR_PART_Z_COMPARATOR = new Comparator<CharacterPart>() {

        public int compare(CharacterPart o1, CharacterPart o2) {
            return o1.z - o2.z;
        }
        
    };

    @Override
    public void doRender(RenderSpace g) {
        if (morph != null) {
            morph.doRender(this, g);
            return;
        }
        
        if (lastRenderedStance != stance || lastFrame != frame || partsDirty) {
            currentParts.clear();
            // provide skin
            skinProvider.provide(currentParts, stance, frame);
            // provide hair
            if (hairProvider != null) {
                hairProvider.provide(currentParts, stance, frame);
            }
            if (faceProvider != null) {
                faceProvider.provide(currentParts, stance, frame);
            }
            // provide equips
            for (final CharacterPartsProvider provider : equipsProvider.values()) {
                provider.provide(currentParts, stance, frame);
            }
            
            // set state
            lastRenderedStance = stance;
            lastFrame = frame;
            partsDirty = false;
        }

        this.update();
        this.constrainFrame();

        if (renoria.Core.systemTime() - getLastTalk() > 3500) {
            chatBubble = null;
        }

        Rectangle r = getArea();
        
        boolean flip = direction == ObjectDirection.RIGHT && !noFlip();
        
        for (Iterator<CharacterPart> i = currentParts.iterator(); i.hasNext();) {
            CharacterPart part = i.next();
            int charX = x + r.width/2 - Core.view.x;
            int charY = y + r.height - Core.view.y;
            part.draw(charX, charY, g, flip);
        }
        
        // [EFFECTS]
        for (int i = 0; i < effects.size(); i++) {
            effects.get(i).doRender(g);
        }
        // [END EFFECTS]
        
        //drawDebugBoundaries(g);
    }

Basically characters are composed up from parts, for example arm, body, head, weapon, etc and this code does the Z-layering and providing for me. Providers are shared between characters, so if char1 and char2 are wearing the same weapon, they will be attached to the same provider. (Saves memory)

RenderSpace is a class I devised as an abstraction between Java2D and JOGL, but it can be extended to any Java API that can do rendering, for example LWJGL.

Just wanted to see how you optimised to get 10k+ objects at 60 fps with Java2D. Can you please post the relevant parts where you did optimisations?

Alright so here are the relevant parts:

To convert BufferedImages to device-compatible images: (Extreme speed booster)


    public static BufferedImage toCompatibleImage(BufferedImage image, boolean override, boolean disposeOld) {
        if (image.getColorModel().equals(gfxConfig.getColorModel()) && !override) {
            return image;
        }
        BufferedImage newImage = gfxConfig.createCompatibleImage(image.getWidth(), image.getHeight(), image.getTransparency());
        Graphics2D g2d = newImage.createGraphics();
        g2d.drawImage(image, 0, 0, null);
        g2d.dispose();
        if (disposeOld) {
            image.flush();
        }

        return newImage;
    }

The Active-Rendering Canvas - uses BufferStrategy:


drawCanvas = new RenoriaCanvas();
        drawCanvas.setSize(RenoriaSize);
        drawCanvas.setPreferredSize(RenoriaSize);
        panel.add(drawCanvas);
        drawCanvas.setFocusTraversalKeysEnabled(false);
        drawCanvas.requestFocus();
        drawCanvas.setIgnoreRepaint(true);
if (Core.useDrawBuffers.getValue()) {
            drawCanvas.createBufferStrategy(RenoriaOptions.BufferCount.getValue());
            this.buffer = drawCanvas.getBufferStrategy();
        } else {
            drawBuffer = ImageFactory.gfxConfig.createCompatibleImage(RenoriaSize.width, RenoriaSize.height, BufferedImage.OPAQUE);
            drawBuffer = ImageFactory.gfxConfig.createCompatibleImage(RenoriaSize.width, RenoriaSize.height, BufferedImage.OPAQUE);
        }
        this.renderSpace = new RenderSpace();

Rendering to the Canvas: (Use another thread to call render() repeatedly)


public void render() {
            if (!render) {
                return;
            }
            if (drawBuffer != null) {
                renderInternal(drawBuffer.createGraphics());
                drawCanvas.update(drawCanvas.getGraphics());
                return;
            }
            Graphics2D g = (Graphics2D) buffer.getDrawGraphics();
            renderInternal(g);
            buffer.show();
            if (RenoriaOptions.UseVsync.getValue()) {
                Toolkit.getDefaultToolkit().sync();
            }

    }

Use a QuadTree to draw only whats required:


public void doRender(RenderSpace g) {
	TreeSet<MapObject> objList = new TreeSet<MapObject>(MAP_OBJECT_Z_COMPARATOR);
	currentMap.getObjectsInRegionQuadTree(Core.viewX, Core.viewY, Core.viewWidth, Core.viewHeight, objList);
	
	for (Iterator<MapObject> i = objList.iterator(); i.hasNext();) {
		i.next().doRender(g);
	}
}

Hope this helps :slight_smile:

I just didn’t do the [icode]toCompatibleImage()[/icode] part. Without that, I’ve got 302 fps on my old laptop without a graphics card.

Now I had got a pc with an NVIDIA card and it boosted upto 750 fps. I didn’t had the same example now but I’ll try by including that fix. Would have to try. Thanks.

EDIT:
I thought I’ve lost the code, but it’s present in my backups. I’ll test it tomorrow morning.

I don’t quite understand why you’d want more than 60 FPS to be honest, most monitors only refresh at 60Hz anyway so any subsequent updates you write to the frame buffer won’t even be seen by the user?

Am not saying that I want more fps. This fps is for a game where there is only one screen with a very few objects. And if I keep on increasing the number of objects, it gets too less. You are asking why more than 60, right? Try to answer why not more? Nowadays, we’re having more processing powers, more graphics power, so the best bet is to do required amount of logic updates and always try to use the remaining time to get very high fps. I’ll calculate the collisions every frame and so I can have much smoother game play and a very fast collision response.

You need to check whether your bottleneck is in Java2D or your other logic code :slight_smile:

When Java2D is used correctly it can yield super fast framerates as you can see, I think converting the buffered images to the device native format will give you a considerable speed boost.

Either way, I don’t try to render higher than 80 FPS. My code has a Thread.sleep( ) that will limit the framerate to 80 FPS. Without the sleep it can yield higher framerates, but I just don’t see the need for a super high framerate.

It’s a bit like saying we have 32 bit color but why not 64 bit color? Because the human eye can only distinguish about 4 billion different colours, therefore more would be pointless.
Same thing applies to FPS. I say 60 FPS is the sweet spot.

Here is a tech demo video of the MMO and engine consisting of:

  • Monster attacking (melee and ranged)
  • Item equipping and dropping
  • Character movement and synchronisation
  • A demo of me spawning 1000 monsters and killing them all with ranged throwing stars. (Shows just how well the engine performs.)
  • Player chatting
  • Some effects that the engine can support :slight_smile:

YtkQmY0sgF8

Very nice, i would like to play the alpha.
This game looks very much like maplestory, i hope you can make it better then that.

UPDATE:

Our first boss is in game! :slight_smile:

He’s using powerups Boost Attack and Boost Defence.

Also, monster projectiles (such as dragonfire) are now coded and work.

UPDATE:

King Frost Dragon now has special attacks like the following:

Let me know what you think of our animations!

Attack1: Tail whip, stuns everyone standing on the ground, attack+5000, shakes the map.
Attack2: Fire breath, shoots dragonfire that will burn anyone it hits, damage over time for 30 seconds.

All this is perfectly synchronized between clients (ie Player1 and Player2 will see the same attack animation at the exact same time, assuming no latency)

Also, better monster editor: