I've created a new abstract class "Combatant" which inherits from MapObject and is now the base class for both the old PlayerCharcter and the new Mob class. A combatant is everything which can attack or be attacked.
I've moved all player-character-specific logic from MapObjectController to a new PlayerCharacterController which calls the MapObjectController when a player wants to do something. That way I can keep the MapObjectController as generic and flexible as possible.
I already created the stub of a MobController. All monster AI will be implemented there. Implementing the AI in a wider scope than inside the Mob class will allow me to implement a Mob AI which is much more aware of what's happening around it.
Wednesday, October 26, 2011
Monday, October 24, 2011
Character sprite list
2 genders
5 classes
= 10 characters.
I have free movement, so I will need 8-directional sprites or it will look silly. Three directions can be mirrored, so I got 5 view angles (front, sidefront, side, sideback, back).
Each of these 5 angles needs the following phases:
1 stand
1 death
1 sit
4 walk
2 attack
= 9 phases.
That's 45 frames per character.
That are 450 frames in total for player characters. Quite a lot of work. When I ever go commercial I might want to outsource that to a graphic designer. But for now I need a single character as proof of concept.
I will only have customization where easy to do. Hair, headgear, back (capes for the guys, wings for the girls) and weapons. Maybe palette swaps for torso and pants when I can do it at runtime.
5 classes
= 10 characters.
I have free movement, so I will need 8-directional sprites or it will look silly. Three directions can be mirrored, so I got 5 view angles (front, sidefront, side, sideback, back).
Each of these 5 angles needs the following phases:
1 stand
1 death
1 sit
4 walk
2 attack
= 9 phases.
That's 45 frames per character.
That are 450 frames in total for player characters. Quite a lot of work. When I ever go commercial I might want to outsource that to a graphic designer. But for now I need a single character as proof of concept.
I will only have customization where easy to do. Hair, headgear, back (capes for the guys, wings for the girls) and weapons. Maybe palette swaps for torso and pants when I can do it at runtime.
Sunday, October 23, 2011
How could I do scripting?
Yes, this milestone is pretty far ahead, but I still wonder how I could do scripting.
Do I even need scripting? You usually add a scripting engine to a game engine so that you have a simpler work environment for team members which are less skilled at programming. But I am working alone. So why not just create a simple framework to define quests in Java and leave it as that? Could be much less work because I wouldn't have to worry about script bindings and could interact with the server engine directly.
A drawback would be that I would not be able to change quest coding at runtime. I would have to rebuild the server for every single script change (Although Manaserv has the same problem even though it uses Lua as a scripting backend). Also when I ever do hire people to help me with content or I would like to license the server to someone without revealing the sourcecode, I would need to give them a way to do stuff without any hardcoding.
An alternative to implementing a full scripting backend with a real scripting language would be to use some kind of markup language like JSON or XML to describe NPC dialog trees and other frequent use-cases for scripting. That way I could move the trivial stuff to external files and not clutter the servers codebase with hundreds of trivial NPC dialogs.
This is an example how I could do a simple dialog with JSON:
Explanation:
dialog: Process the following elements as dialog.
dialog->setvar: Set the variable as key to the value
choice: present the elements of this array to the user as dialog options:
choice->option: dialog option text the user can select
choice->dialog: (optional) process the following dialog when the user clicks this option
choice->next: what to do after processing the dialog. "back": go back to choice. "continue": continue after the choice. "quit": leave the dialog.
*->only_when_equal: ignore this element, when the variable is unequal to the value
Do I even need scripting? You usually add a scripting engine to a game engine so that you have a simpler work environment for team members which are less skilled at programming. But I am working alone. So why not just create a simple framework to define quests in Java and leave it as that? Could be much less work because I wouldn't have to worry about script bindings and could interact with the server engine directly.
A drawback would be that I would not be able to change quest coding at runtime. I would have to rebuild the server for every single script change (Although Manaserv has the same problem even though it uses Lua as a scripting backend). Also when I ever do hire people to help me with content or I would like to license the server to someone without revealing the sourcecode, I would need to give them a way to do stuff without any hardcoding.
An alternative to implementing a full scripting backend with a real scripting language would be to use some kind of markup language like JSON or XML to describe NPC dialog trees and other frequent use-cases for scripting. That way I could move the trivial stuff to external files and not clutter the servers codebase with hundreds of trivial NPC dialogs.
This is an example how I could do a simple dialog with JSON:
{"dialog": [ { message: "Hello" }, { message: "How can I help you?" }, { choice: [ { option: "Where is the castle?", dialog: [ { message: "Just down the road, you can't miss it." } ], next: "back" }, { only_when_equal { "quest_slay_dragon", false }, option: "I need a job", dialog: [ { message: "I got a quest for you." } { message: "slay the dragon in the mountains." } { choice: [ { option: "No, that's too dangerous!" , next: "back" }, { option: "Sure, let's go!", dialog: [ { setvar: { "quest_slay_dragon", true } }, { message: "good luck, then" } ], next: "quit" } ] } ], next: "back" }, { only_when_equal { "quest_slay_dragon", true }, option: "I need a job", dialog: [ { message: "I already told you to slay the dragon!" } ], next: "back" } { option: "Nothing, goodbye", next: "continue" }, ]}, { message: "It was a pleasure to meet you." }, { only_when_equal: { "quest_slay_dragon", true }, message: "And good luck with the dragon!" } ]}
Explanation:
dialog: Process the following elements as dialog.
dialog->setvar: Set the variable as key to the value
choice: present the elements of this array to the user as dialog options:
choice->option: dialog option text the user can select
choice->dialog: (optional) process the following dialog when the user clicks this option
choice->next: what to do after processing the dialog. "back": go back to choice. "continue": continue after the choice. "quit": leave the dialog.
*->only_when_equal: ignore this element, when the variable is unequal to the value
Milestone 3 almost reached, time for some planning
Milestone 3 is practically completed on the server-side. The client side needs some finishing touches, but the critical stuff works.
So time for a new roadmap.
Milestone 4 (interaction):
* Mobs
* Basic fighting (mobs won't fight back and die after a fixed number of hits)
Milestone 5 (exploration):
* Walkable and unwalkable tiles (no route finding yet)
* Fringe objects
* Map editing (I think I will integrate the map editor right into the client)
Milestone 6 (growth):
* EXP gain
* Combat mechanics
* Mob AI
Milestone 7 (wealth):
* Items and Inventory
* Player trading
* Shops
Milestone 8 (adventuring):
* NPC interaction
* Scripting (?)
Milestone 9 (security):
* Permission management
I also need to start to work on content. It's time to create some tiles and sprites. I also need to design the setting of my game world.
So time for a new roadmap.
Milestone 4 (interaction):
* Mobs
* Basic fighting (mobs won't fight back and die after a fixed number of hits)
Milestone 5 (exploration):
* Walkable and unwalkable tiles (no route finding yet)
* Fringe objects
* Map editing (I think I will integrate the map editor right into the client)
Milestone 6 (growth):
* EXP gain
* Combat mechanics
* Mob AI
Milestone 7 (wealth):
* Items and Inventory
* Player trading
* Shops
Milestone 8 (adventuring):
* NPC interaction
* Scripting (?)
Milestone 9 (security):
* Permission management
I also need to start to work on content. It's time to create some tiles and sprites. I also need to design the setting of my game world.
WinJS really not much fun
I am currently mostly doing client stuff. I added multiple screens to the client. I got three different screens now. The login page, the character selection and the game itself. You can create an account, log in and choose a character, but you can't create a new character yet. Also switching characters and accounts doesn't work like it should.
Working with Microsofts new API really isn't much fun yet. The documentation is mostly incomplete and where it isn't incomplete it is extremely brief. It really isn't good for more than looking up the methods of an object. When you want to know what exactly a method does, or more importantly what this is good for, you are pretty lost.
The API samples, on the other hand, are so convoluted that they are hard to follow. They squeeze about five different use cases into each sample, making it hard to tell the essential lines from the fluff.
And because it's such a new technology, there aren't any sources of information about it except for the MSDN. Nobody outside of Microsoft has written any tutorials yet and useful forum threads are also hard to find.
Fortunately many things which work when programming javascripts for normal websites also work for metro apps. So you get pretty far when you just pretend you are building a website and apply existing javascript know-how. This does, however, leave the sour aftertaste that you are missing out on all the nifty new features like data-bindings or the Metro-specific GUI controls, which could surely solve your problems much more elegantly and efficiently... when you would understand how to use them.
Working with Microsofts new API really isn't much fun yet. The documentation is mostly incomplete and where it isn't incomplete it is extremely brief. It really isn't good for more than looking up the methods of an object. When you want to know what exactly a method does, or more importantly what this is good for, you are pretty lost.
The API samples, on the other hand, are so convoluted that they are hard to follow. They squeeze about five different use cases into each sample, making it hard to tell the essential lines from the fluff.
And because it's such a new technology, there aren't any sources of information about it except for the MSDN. Nobody outside of Microsoft has written any tutorials yet and useful forum threads are also hard to find.
Fortunately many things which work when programming javascripts for normal websites also work for metro apps. So you get pretty far when you just pretend you are building a website and apply existing javascript know-how. This does, however, leave the sour aftertaste that you are missing out on all the nifty new features like data-bindings or the Metro-specific GUI controls, which could surely solve your problems much more elegantly and efficiently... when you would understand how to use them.
Tuesday, October 18, 2011
Raid system: The cave of eternity
Randomly assigned parties progress through an endless series of small, auto-generated dungeon levels. Players can join existing parties at any time.
To get to the next instance, all monsters in an instance must be defeated. Then the party is automatically teleported into the next.
The larger and more powerful the party, the stronger the enemies, but also the bigger the rewards (even when divided through the number of players), so that large parties are encouraged. But because the monster power scales with the number of players, good teamwork is required for large groups.
Rewards are given at the end of each instance, but only to those which participated from the start of the instance.When you die, you can't rejoin the party until after the next instance has started. This means you miss out two rewards. You can, however, join a different party immediately and miss only one. By encouraging switching over waiting to stay, parties are encouraged not to let people die and to create a friendly environment where people want to rejoin, otherwise the group will lose members and get worse rewards.
To spice things up, there will be rarely encountered special rooms, like boss rooms, extra hard "danger rooms" with deadly traps or "treasure chambers" with freebies for everyone.
To get to the next instance, all monsters in an instance must be defeated. Then the party is automatically teleported into the next.
The larger and more powerful the party, the stronger the enemies, but also the bigger the rewards (even when divided through the number of players), so that large parties are encouraged. But because the monster power scales with the number of players, good teamwork is required for large groups.
Rewards are given at the end of each instance, but only to those which participated from the start of the instance.When you die, you can't rejoin the party until after the next instance has started. This means you miss out two rewards. You can, however, join a different party immediately and miss only one. By encouraging switching over waiting to stay, parties are encouraged not to let people die and to create a friendly environment where people want to rejoin, otherwise the group will lose members and get worse rewards.
To spice things up, there will be rarely encountered special rooms, like boss rooms, extra hard "danger rooms" with deadly traps or "treasure chambers" with freebies for everyone.
Things to avoid
Now that my basics progress so smoothly I need to think about how to design my gameplay.
My platform are Windows Metro apps on tablet computers. Tablets aren't like desktops. You don't sit in front of them for hours. You want quick entertainment. You want to use them on the train to kill time, where you suddenly have to stop playing. For these reasons I need to avoid:
#2 can be solved by having lots of quests with regular rewards.
#3 and #4 will be hard. I need a good auto-grouping system and a raid system which allows you to hop on and off at any time without putting yourself or the group at a disadvantage or missing all the content.
#5 will be easy to do simply by not doing the opposite.
#6 means quick progress, no activities which require more than an hour, and no waiting for something (health to regenerate, raid to start, mob to spawn) for longer than a minute.
My platform are Windows Metro apps on tablet computers. Tablets aren't like desktops. You don't sit in front of them for hours. You want quick entertainment. You want to use them on the train to kill time, where you suddenly have to stop playing. For these reasons I need to avoid:
- Long travel times. It got to be easy for the player to get to where they want to be.
- Long periods of grinding
- Making it hard to find a group
- Making it irresponsible to leave a group once you joined it
- Punish the player for logging out suddenly
- not catering to casual gamers
#2 can be solved by having lots of quests with regular rewards.
#3 and #4 will be hard. I need a good auto-grouping system and a raid system which allows you to hop on and off at any time without putting yourself or the group at a disadvantage or missing all the content.
#5 will be easy to do simply by not doing the opposite.
#6 means quick progress, no activities which require more than an hour, and no waiting for something (health to regenerate, raid to start, mob to spawn) for longer than a minute.
Sunday, October 16, 2011
Good progress with milestone 3
The server can now communicate with MongoDB. The save and load methods of my Database class can theoretically work with any object, because it examines the annotations of the class of the object.
When I want an object to be store-able in the database, I just add my @DatabaseObject annotation to the class and my @DBMapping annotation to every variable of it I want to save. I can also bind getters and setters to database fields by adding my @DBGetter or @DBSetter annotation to them.
That way it will be easy to add and remove attributes of classes stored in the database.
I can already create accounts and characters, log in with name and password and choose a character.
Which message is acceptable in which state of the connection is determined in ClientConnection. That class is getting more and more bloated. I think the login and character selection handling needs to be moved to another class soon.
Todo:
So much about the server. The client, on the other hand, is lagging behind feature-wise. Everything I boasted about above can only be used through forging JSON messages in the debug chat tab. There is no GUI for entering account info or selecting a character. Implementing that will be my next step.
When I want an object to be store-able in the database, I just add my @DatabaseObject annotation to the class and my @DBMapping annotation to every variable of it I want to save. I can also bind getters and setters to database fields by adding my @DBGetter or @DBSetter annotation to them.
That way it will be easy to add and remove attributes of classes stored in the database.
I can already create accounts and characters, log in with name and password and choose a character.
Which message is acceptable in which state of the connection is determined in ClientConnection. That class is getting more and more bloated. I think the login and character selection handling needs to be moved to another class soon.
Todo:
Create indices in the database at startup, so that account and usernames must be unique- When creating a character, it can not be selected unless the account logs in and out again.
- Characters are only saved at logout - they should be saved more regularly in case of a crash (but not all at once to avoid load spikes on the database)
- changing accounts and characters is shaky
So much about the server. The client, on the other hand, is lagging behind feature-wise. Everything I boasted about above can only be used through forging JSON messages in the debug chat tab. There is no GUI for entering account info or selecting a character. Implementing that will be my next step.
Saturday, October 15, 2011
Network Debug Tabs
These two new tabs will definitely go when the client is released, but until then they will surely come in handy:
Preloading Images
The input of the "Debug NetIn" tab can also be used to send strings to the server and thus communicate with it directly on the protocol level. This function will make testing of server features a lot easier.
The new chat
The new chat control of the client with multiple tabs and scrollbar.
"Map" is the active tab. "System" is inactive. "Global" is also inactive, but has unread text.
Now the chat is pretty much usable.
"Map" is the active tab. "System" is inactive. "Global" is also inactive, but has unread text.
Now the chat is pretty much usable.
Milestone 2 reached!
Differing between local and global chat is implemented. I decided to forget about sending messages to only those nearby and go for the one-map-one-channel approach instead. It's not like it would be hard to do (I am sending the chat message in the MapInstance class where I have access to all objects and their positions, and calculating their distance would be trivial) - It's just that I can't decide what a reasonable distance is because I don't know how big the screen of the user is. And even when I would know: everyones screen is different, but I need to use the same distance for everyone, because you expect that when you can hear someone, he can hear you too. So when I have to go for a one-size-fits-all chat distance anyway, I could just take the whole map for it.
The chat support on the client is still very spartan. To send a global message, you prefix it with a "#" symbol. It would be better to have separate tabs for global and map chat. The chat window needs some love anyway, because currently it can't even scroll. It just grows and grows instead.
Then it will be finally time for Milestone 3: Login sequence and persistent accounts and characters. And when that's finished I can finally start with implementing real gameplay.
The chat support on the client is still very spartan. To send a global message, you prefix it with a "#" symbol. It would be better to have separate tabs for global and map chat. The chat window needs some love anyway, because currently it can't even scroll. It just grows and grows instead.
Then it will be finally time for Milestone 3: Login sequence and persistent accounts and characters. And when that's finished I can finally start with implementing real gameplay.
Classes
What does the class do?
Class list
I am still indifferent whether to have a healer-type support class, because they are usually horribly imbalanced towards party-play. They are unplayable without a party, but parties are unplayable without them. I would rather prefer when every class would be equally playable solo and also be an equally-valuable addition to a party. Still, healers are the favorite of every socializer-type player.
So which class is the dedicated tank? Fighter, Rogue and Berserker are supposed to be equally good tanks. Fighters because they have the best armor and thus don't get much damage, Rogues because they have the best agility and thus get rarely hit and Berserker because they have the best HP and HP regeneration and thus don't care about being hit.
- Which items you can equip on each level, and because you get your skills from your weapon, also the skills you can use
- Your stat growth with each level
- The sprite your character is using
Class list
Class | Weapon | Skills | Role in a party |
---|---|---|---|
Fighter | Sword | Buffs through inspirational speeches and battle cries, single- and multi-target damage | Bolster party attributes |
Rogue | Dagger | confuse enemies reducing their dodge rate, rend enemies armor, single-target damage | Make it easier to damage enemies |
Berserker | Mace/Axe | Intimidate enemies reducing their damage, stun them or push them around, multi-target damage | Preventing enemies from doing damage |
Wizard | Wand | Ranged multi-target damage spells | Damage dealer vs. groups of enemies |
Archer | Bow | Special arrows dealing massive damage to single targets | Damage dealer vs. single enemies |
I am still indifferent whether to have a healer-type support class, because they are usually horribly imbalanced towards party-play. They are unplayable without a party, but parties are unplayable without them. I would rather prefer when every class would be equally playable solo and also be an equally-valuable addition to a party. Still, healers are the favorite of every socializer-type player.
So which class is the dedicated tank? Fighter, Rogue and Berserker are supposed to be equally good tanks. Fighters because they have the best armor and thus don't get much damage, Rogues because they have the best agility and thus get rarely hit and Berserker because they have the best HP and HP regeneration and thus don't care about being hit.
Character leveling
While I want a complex equipment leveling system, I would prefer to keep the character leveling straight-forward and without many consequence-heavy choices.
Many MMORPGs make the mistake of asking the player to distribute stat points with every level-up without them actually knowing what each stat does and what's the ideal distribution. They force players to make decisions before they can know the consequences, and when they make the wrong decision, they punish them with badly performing characters. But they aren't punished immediately, they are punished weeks or months after they made the decision when they notice that other characters perform much better because they didn't make the mistake of wasting points on useless stats.
The only way for the player to avoid this is by consulting strategy guides before even playing. I don't think that this is good. Players should be able to master a game on their own without consulting meta-information.
Some games try to reduce the impact of this design flaw by offering stat resets, but this is basically reductio ad absurdum. What point does character development in a certain direction have, when you can change the direction of your character at any time?
Stat distribution is simply broken by design! For that reason I won't have any free stat distribution. Every character will gain character stats automatically with each level-up according to its class. The game complexity will be mostly in the item system, because items are just the steps on the ladder to character advancement. It doesn't hurt as much to discard them when they don't work as expected as it hurts to discard a gimped character.
Many MMORPGs make the mistake of asking the player to distribute stat points with every level-up without them actually knowing what each stat does and what's the ideal distribution. They force players to make decisions before they can know the consequences, and when they make the wrong decision, they punish them with badly performing characters. But they aren't punished immediately, they are punished weeks or months after they made the decision when they notice that other characters perform much better because they didn't make the mistake of wasting points on useless stats.
The only way for the player to avoid this is by consulting strategy guides before even playing. I don't think that this is good. Players should be able to master a game on their own without consulting meta-information.
Some games try to reduce the impact of this design flaw by offering stat resets, but this is basically reductio ad absurdum. What point does character development in a certain direction have, when you can change the direction of your character at any time?
Stat distribution is simply broken by design! For that reason I won't have any free stat distribution. Every character will gain character stats automatically with each level-up according to its class. The game complexity will be mostly in the item system, because items are just the steps on the ladder to character advancement. It doesn't hurt as much to discard them when they don't work as expected as it hurts to discard a gimped character.
Equipment leveling system
This is my plan for the equipment system.
All equip-able items have an experience counter and can level up.
When the character receives experience, half of it goes to the character and half of it is divided evenly between the equipped items (unless it isn't wearing any, then all goes to the character).
Equipment has various stats, and a hidden growth to each stat which is randomly generated at creation of the item. When an item levels up, each stat grows randomly between 0 and its growth, so the player has to level an item a few times to see if it has potential or not.
In addition to the stat growth, an armor piece will gain a random bonus to a random character stat with each level-up. Higher levels and better items give higher bonuses. Weapons will gain randomly generated bonus special attacks instead.
The required experience per level depends on the total stat growth, so weaker items will level faster.
Items have a maximum level. When the maximum level is reached, their share of experience is given to the character.
Result:
The fastest way to level a piece of equipment is wearing only one piece so it gets maximum experience. But that way the character will be weak and will only able to fight lower enemies. So itself won't level fast. This play-style will be preferred by perfectionists.
The fastest way to level the character is by quickly maxing out items with bad stat growth and wearing them so that the character receives 100% experience. This play-style will be preferred by people who want to advance quickly. But it won't be sustainable for long because sooner or later the equipment will need to be replaced by items of a higher tier.
Salvaging items:
A leveled item can be salvaged. When the character does so, the item is destroyed and the character receives crystals representing the attribute bonuses or special attacks the item has gained with level-ups. These crystals can be used on an item of the same type to replace a stat bonus or special attack of the same level. This gives the player the option to use good bonuses achieved with lower tier equipment to replace bad or useless bonuses in higher tier equipment. It also opens another way to create the perfect equipment: by training multiple identical items and picking the best bonuses from them.
This not just gives an incentive to train lower-tier items before advancing to the next item tier, it also creates a drain which removes used items from the game and forces newcomers to level good items themselves instead of buying them from higher level players.
All equip-able items have an experience counter and can level up.
When the character receives experience, half of it goes to the character and half of it is divided evenly between the equipped items (unless it isn't wearing any, then all goes to the character).
Equipment has various stats, and a hidden growth to each stat which is randomly generated at creation of the item. When an item levels up, each stat grows randomly between 0 and its growth, so the player has to level an item a few times to see if it has potential or not.
In addition to the stat growth, an armor piece will gain a random bonus to a random character stat with each level-up. Higher levels and better items give higher bonuses. Weapons will gain randomly generated bonus special attacks instead.
The required experience per level depends on the total stat growth, so weaker items will level faster.
Items have a maximum level. When the maximum level is reached, their share of experience is given to the character.
Result:
The fastest way to level a piece of equipment is wearing only one piece so it gets maximum experience. But that way the character will be weak and will only able to fight lower enemies. So itself won't level fast. This play-style will be preferred by perfectionists.
The fastest way to level the character is by quickly maxing out items with bad stat growth and wearing them so that the character receives 100% experience. This play-style will be preferred by people who want to advance quickly. But it won't be sustainable for long because sooner or later the equipment will need to be replaced by items of a higher tier.
Salvaging items:
A leveled item can be salvaged. When the character does so, the item is destroyed and the character receives crystals representing the attribute bonuses or special attacks the item has gained with level-ups. These crystals can be used on an item of the same type to replace a stat bonus or special attack of the same level. This gives the player the option to use good bonuses achieved with lower tier equipment to replace bad or useless bonuses in higher tier equipment. It also opens another way to create the perfect equipment: by training multiple identical items and picking the best bonuses from them.
This not just gives an incentive to train lower-tier items before advancing to the next item tier, it also creates a drain which removes used items from the game and forces newcomers to level good items themselves instead of buying them from higher level players.
Warping works
Map warps work now.
I created a MapFactory class, which creates the map layouts. Later this class will provide both maps loaded from files and autogenerated maps, but currently it has two methods makeBrownMap and makeGreenMap, which return hardcoded map layouts made entirely of dirt and grass tiles. These maps also come with a warp to the center of the other map visually indicated by tiles of the other type.
The warp areas themselves are handled by a new map instance controller, the WarpController, which receives InterMessagePositionChange messages from the MapObjectManager whenever it changes the position of an object. The WarpController then checks if it's inside one of its warps, and when it does, it dispatches a InterMessageCharacterMapChange message. The MapInstance has subscribed to this message on the local dispatcher of each map and can thus initiate the transfer from the old to the new map instance.
So what's next for this milestone? Ah, local chatting. That one is interesting, because I want chat messages to go only to those nearby and not to everyone on the same map. That means I can not just use the local dispatcher. This concept of area-confined messages might also be interesting for object movement information, because informing the clients only about objects on their screens instead of everything on the map could conserve bandwidth and also hide information the user is not supposed to know (player killers in PvP, for example, would greatly enjoy a hack which shows them everyone on the map, while their victims don't notice the PKer before it appears on the screen). Or I just go the other way around and add a minimap to the client which shows all objects on the map.
I created a MapFactory class, which creates the map layouts. Later this class will provide both maps loaded from files and autogenerated maps, but currently it has two methods makeBrownMap and makeGreenMap, which return hardcoded map layouts made entirely of dirt and grass tiles. These maps also come with a warp to the center of the other map visually indicated by tiles of the other type.
The warp areas themselves are handled by a new map instance controller, the WarpController, which receives InterMessagePositionChange messages from the MapObjectManager whenever it changes the position of an object. The WarpController then checks if it's inside one of its warps, and when it does, it dispatches a InterMessageCharacterMapChange message. The MapInstance has subscribed to this message on the local dispatcher of each map and can thus initiate the transfer from the old to the new map instance.
So what's next for this milestone? Ah, local chatting. That one is interesting, because I want chat messages to go only to those nearby and not to everyone on the same map. That means I can not just use the local dispatcher. This concept of area-confined messages might also be interesting for object movement information, because informing the clients only about objects on their screens instead of everything on the map could conserve bandwidth and also hide information the user is not supposed to know (player killers in PvP, for example, would greatly enjoy a hack which shows them everyone on the map, while their victims don't notice the PKer before it appears on the screen). Or I just go the other way around and add a minimap to the client which shows all objects on the map.
Thursday, October 13, 2011
Multithreading Mayhem
While multithreading solves a lot of problems, it also causes a bunch of new ones.Whenever you change the state of the data another thread operates on, you risk that you change it while said thread reads it, which means that it will read a mixed version between old and new state.
When you expect this to happen (and chances that you forget about it are high) you can solve it with a lock. Adding a lock to the data means that one thread has to wait with reading or manipulating a piece of data until the other thread is finished.
But using locks can cause another problem: Deadlocks!
In my server I have currently two kinds of objects which have threads: ClientConnection and MapInstance. The client connection is unlikely to have problems, because the thread is only sending data to other threads, not receiving from them. Receiving data is a synchronous process. By declaring the send method as synchronized I won't have to worry about messages mixing up when multiple threads try to send data to the client at the same time.
But the map instances are a more serious problem. Their data is manipulated by a bunch of controllers they own (currently only one, but more are about to come). I am glad that processing inside the MapInstance class is single-threaded, because the controllers are processed in order and they are not supposed to communicate with the outer world except through the MapInstance class. But at any time during processing, the maps MessageDispatcher can receive a message from another thread, which is immediately passed to the controllers, which could currently be inside of their processing loop.
To prevent this from happening I could:
A) When the MessageDisptcher is delivering a message to a controller, make it check the controller if it is currently busy and when it is wait for it to finish before delivering the message. But this might cause serious delays or even deadlocks in message processing.
B) separate receiving messages from processing messages in the controllers. That way a controller can store a message but don't actually do anything with it until its update method is called. At the beginning of its update, every controller should:
Then I just have to make sure that controllers don't communicate with the outside world except through the message queue.
When you expect this to happen (and chances that you forget about it are high) you can solve it with a lock. Adding a lock to the data means that one thread has to wait with reading or manipulating a piece of data until the other thread is finished.
But using locks can cause another problem: Deadlocks!
In my server I have currently two kinds of objects which have threads: ClientConnection and MapInstance. The client connection is unlikely to have problems, because the thread is only sending data to other threads, not receiving from them. Receiving data is a synchronous process. By declaring the send method as synchronized I won't have to worry about messages mixing up when multiple threads try to send data to the client at the same time.
But the map instances are a more serious problem. Their data is manipulated by a bunch of controllers they own (currently only one, but more are about to come). I am glad that processing inside the MapInstance class is single-threaded, because the controllers are processed in order and they are not supposed to communicate with the outer world except through the MapInstance class. But at any time during processing, the maps MessageDispatcher can receive a message from another thread, which is immediately passed to the controllers, which could currently be inside of their processing loop.
To prevent this from happening I could:
A) When the MessageDisptcher is delivering a message to a controller, make it check the controller if it is currently busy and when it is wait for it to finish before delivering the message. But this might cause serious delays or even deadlocks in message processing.
B) separate receiving messages from processing messages in the controllers. That way a controller can store a message but don't actually do anything with it until its update method is called. At the beginning of its update, every controller should:
- Lock its message queue
- Copy its message queue into a buffer
- Unlock its message queue
- Process the buffered queue
Then I just have to make sure that controllers don't communicate with the outside world except through the message queue.
Monday, October 10, 2011
Concept for pathfinding
I don't need this yet because I don't have collision yet and I don't intend to add it so soon, but I want to write down the concept.
While my world itself is tile-based, movement in my game world isn't. Characters can move with floating point precision. I plan to use touchscreen/mouse as the primary input, so it would feel strange when they would be bound to 8-directional movement.
For that reason using A* using each tile as a node is not an option. But how can I do pathfinding in such an environment?
First I need to build a graph of nodes which have a direct line to each other:
So how does the pathfinding work? I can just use Dijkstra's algorithm for finding the shortest list of nodes from A to B. This will give me a short route, but one which very likely has still room for improvement, because it will only allow me to move from rectangle to rectangle at the corner points (blue lines). When there would be a straight line (red line), this solution might be suboptimal.
But how do I get from the blue line to the red one?
I just traverse the nodes of my path, and look if there is a direct line of sight between the previous node and the next node. When there is, I replace the current node with a direct connection between the two (Are there cases where I need to repeat that multiple times? I am not sure...). Afterwards I should have a direct line where possible and a very naturally-looking path.
Finding out whether a line collides with a rectangle can be found out by checking if it intersects its diagonals (assuming both ends of the line are outside of the rectangle, which can be assured in this case). Line intersection can be implemented using this algorithm.
While my world itself is tile-based, movement in my game world isn't. Characters can move with floating point precision. I plan to use touchscreen/mouse as the primary input, so it would feel strange when they would be bound to 8-directional movement.
For that reason using A* using each tile as a node is not an option. But how can I do pathfinding in such an environment?
First I need to build a graph of nodes which have a direct line to each other:
- Divide the world into rectangles which are either walkable or unwalkable. I wrote an article about how to do that for Manaserv.
- Each corner of a walkable rectangle which borders at least one other walkable rectangle is a node.
- Every node is also added to the other walkable rectangles it is adjacent to.
- All nodes on the same rectangle are linked to each other. The weight of the links is the distance calculated by the Pythagorean theorem.
- The nodes in step 4 are merged with the nodes they were created from, concatenating their lists of links. That way the linking between rectangles is made.
So how does the pathfinding work? I can just use Dijkstra's algorithm for finding the shortest list of nodes from A to B. This will give me a short route, but one which very likely has still room for improvement, because it will only allow me to move from rectangle to rectangle at the corner points (blue lines). When there would be a straight line (red line), this solution might be suboptimal.
But how do I get from the blue line to the red one?
I just traverse the nodes of my path, and look if there is a direct line of sight between the previous node and the next node. When there is, I replace the current node with a direct connection between the two (Are there cases where I need to repeat that multiple times? I am not sure...). Afterwards I should have a direct line where possible and a very naturally-looking path.
Finding out whether a line collides with a rectangle can be found out by checking if it intersects its diagonals (assuming both ends of the line are outside of the rectangle, which can be assured in this case). Line intersection can be implemented using this algorithm.
Preloading Images
Sunday, October 9, 2011
Another advantage of JSON: Telnet
The advantage of using a string-based protocol like JSON is that I can use a telnet client to connect to the server and to communicate with it. This allows me to examine and test the netcode directly without having to implement it in the graphical client first. I can craft short JSON messages and read the exact JSON reply.
When something doesn't work, telnetting my server helps me to troubleshoot whether it's my server which sends no or incorrect information or my client which doesn't react to it correctly.
With a binary protocol I would have to use Wireshark to sniff on the connection between client and server and then examine the hex code to get an idea of what's happening.
When something doesn't work, telnetting my server helps me to troubleshoot whether it's my server which sends no or incorrect information or my client which doesn't react to it correctly.
With a binary protocol I would have to use Wireshark to sniff on the connection between client and server and then examine the hex code to get an idea of what's happening.
Saturday, October 8, 2011
Multithreading map architecture
Two design problems of Manaserv are:
- It doesn't do any parallelization, and who does still run a server with just a single CPU core?
- When there is a segmentation fault, the whole server goes down and needs to be restarted manually
Server Roadmap updated
Essentially I reversed the order of the milestones 2 and 3. Login doesn't make sense without persistence and implementing maps and map instancing feels like the natural thing to do next right now. By the way, I plan to keep the map layouts completely server-sided. That way I don't need any fileparsing code on the client and also have the ability to add procedurally generate maps without the client even being aware of that (in fact all maps will be procedurally generated for now, because I don't have a map file format or map editor yet).
Milestone 0:
Milestone 1:
Connecting (unauthenticated)Chatting (global)
Player character movement synchronization
- Multiple maps in multithreading architecture
- Transfering characters between maps
- Local chatting
- Asynchronous persistence layer
- Account creation
- Login with username and password
- Character creation
- Basic fighting
- Server-controlled characters ("NPCs", "Mobs")
- Admin commands
- Scripting
- Multiple choice dialogs
- Items and equipment
- Character advancement
Movement synchronization works
I got it! Multiple clients can log in, each one controls a map object with mouse clicks and the movements are shown on the other clients screens in real-time.
But before I can start to program real gameplay I have to do some server infrastructure first. The character objects currently exist in limbo and are only connected to each other via the movement manager class. I want to have a MapInstance class which manages all objects on it. Every map instance should have its own MovementManager and MessageDispatcher for messages which only concern objects on the same map.
I also have to implement a stateful connection sequence (connected, requesting account, logged into account, requesting character, playing on map). To isolate what a character can and can't do in each state (example: it shouldn't be able to choose a character when it isn't logged in yet) I could connect it to a different, specialized MessageDispatcher in each state.
But before I can start to program real gameplay I have to do some server infrastructure first. The character objects currently exist in limbo and are only connected to each other via the movement manager class. I want to have a MapInstance class which manages all objects on it. Every map instance should have its own MovementManager and MessageDispatcher for messages which only concern objects on the same map.
I also have to implement a stateful connection sequence (connected, requesting account, logged into account, requesting character, playing on map). To isolate what a character can and can't do in each state (example: it shouldn't be able to choose a character when it isn't logged in yet) I could connect it to a different, specialized MessageDispatcher in each state.
Friday, October 7, 2011
JSON!
I converted the chat protocol to use JSON messages. It was much easier and straight-forward than implementing my binary protocol.
I implemented JSON parsing and encoding with JSON.simple on the Java server side. Thanks to Robert from Rojotech for his article A Review of 5 Java JSON Libraries which really helped me to choose JSON.simple over all the other JSON libraries out there.
I implemented JSON parsing and encoding with JSON.simple on the Java server side. Thanks to Robert from Rojotech for his article A Review of 5 Java JSON Libraries which really helped me to choose JSON.simple over all the other JSON libraries out there.
JSON?
I tried to implement a binary protocol the whole evening and it turned out to be a lot harder than expected. The way Java and the Metro API handle binary TCP streams is very different, resulting in very convoluted serialization and deserialization of messages.
I am currently considering to go back to string streams and use JSON to encode my network messages. This might mean some more network traffic though, especially for number-heavy messages.
When I want to tell the client that an object moved to position r:l I could do this with:
2 Bytes "this is a movement package"
4 Bytes beingID
8 Bytes position (2*float)
= 14 Byte.
In JSON this would look like this: {"type":"move", "id":1234,"r":12.000004,"l":15.9999998} which are 60 bytes in ASCII.
This might look like a large difference (1:4), but I must not forget that there is also TCP/IP overhead. When I design the protocol efficiently and send as few messages as possible, most messages will be in a single TCP/IP packet, which has an IP header of 20 bytes and a TCP header of 20 bytes. With this additional protocol overhead, the real comparison is 54 to 100 bytes - less than double.
I could reduce protocol overhead by using UDP instead of TCP which has just 8 byte of protocol overhead and has also better real-time capabilities, but with UDP I have to worry about messages disappearing without a trace or arriving in a different order than they were sent. Dealing with those situations would require additional information to be transfered which would quickly eat up the saved bandwidth.
On the other hand using JSON has a lot of advantages like making it easier to keep the protocol backward-compatible, not having to worry about different data representations and much, much easier parsing on the client side.
I am currently considering to go back to string streams and use JSON to encode my network messages. This might mean some more network traffic though, especially for number-heavy messages.
When I want to tell the client that an object moved to position r:l I could do this with:
2 Bytes "this is a movement package"
4 Bytes beingID
8 Bytes position (2*float)
= 14 Byte.
In JSON this would look like this: {"type":"move", "id":1234,"r":12.000004,"l":15.9999998} which are 60 bytes in ASCII.
This might look like a large difference (1:4), but I must not forget that there is also TCP/IP overhead. When I design the protocol efficiently and send as few messages as possible, most messages will be in a single TCP/IP packet, which has an IP header of 20 bytes and a TCP header of 20 bytes. With this additional protocol overhead, the real comparison is 54 to 100 bytes - less than double.
I could reduce protocol overhead by using UDP instead of TCP which has just 8 byte of protocol overhead and has also better real-time capabilities, but with UDP I have to worry about messages disappearing without a trace or arriving in a different order than they were sent. Dealing with those situations would require additional information to be transfered which would quickly eat up the saved bandwidth.
On the other hand using JSON has a lot of advantages like making it easier to keep the protocol backward-compatible, not having to worry about different data representations and much, much easier parsing on the client side.
Thursday, October 6, 2011
Chatting works!
The first step to an online application is done: Clients can connect to the server I programmed and can chat with each other. The IP:Port will of course be replaced with the character name as soon as it is available.
The next step will be to conceptualize a proper protocol, because currently the server just forwards the text input to all clients without looking at it.
The next step will be to conceptualize a proper protocol, because currently the server just forwards the text input to all clients without looking at it.
Preloading Images
Preliminary server roadmap
Milestone 0:
- Connecting (unauthenticated)
- Chatting (global)
- Player character movement synchronisation
- Local chatting
- Asynchronous persistence layer
- Account creation
- Login with username and password
- Character creation
- Multiple maps in multithreading architecture
- Transfering characters between maps
- Basic fighting
- Server-controlled characters ("NPCs", "Mobs")
- Admin commands
- Scripting
- Multiple choice dialogs
- Items and equipment
- Character advancement
Why a NoSQL database
An online game needs a database for persistent storage. There are countless databases, so which one to pick?
For this project I decided to give the document-oriented database MongoDB a try. Why? Because I want to know what the NoSQL fuss is all about. While some people consider NoSQL the future of data processing, others thinks that it needs to die. Where do I stand in this debate? I don't know. It would be naive to build an opinion about NoSQL databases without ever having used one for a serious project. So I want to give it a try.
What do I hope a document-oriented database will do for me what a relational database can't?
Hopes:
For this project I decided to give the document-oriented database MongoDB a try. Why? Because I want to know what the NoSQL fuss is all about. While some people consider NoSQL the future of data processing, others thinks that it needs to die. Where do I stand in this debate? I don't know. It would be naive to build an opinion about NoSQL databases without ever having used one for a serious project. So I want to give it a try.
What do I hope a document-oriented database will do for me what a relational database can't?
Hopes:
- The lack of schema will allow me to implement changes and new features quickly
- I will discover some killer features which make my life easier
- The lack of schema might cause chaos in my database
- The query language could not be powerful enough to do everything I need to do
- The technology might not be mature yet
Why I decided to program the server in Java
Usually it is a good idea to program the client and the server of a distributed application in the same programming language, because it allows to share code between the two. But in my opinion Javascript is just not suitable for server programming. Sure, there are frameworks like NodeJS which allow to program server applications in Javascript, but this is really not what Javascript was designed for.
When it comes to server programming, you shouldn't take chances. A game server must be secure, because it's an exposed target for hackers. A game server must be stable, because downtimes will annoy everyone. A game server must not have bugs, because a glitch can cause irreversible damage to the gameplay and not just a mild annoyance. And a game server must require few resources, because I will be paying for the hardware. To ensure this, I want to do this in a programming language which fulfills these criteria:
Out of these languages, C++ is the one I have most experienced with. I contributed a lot of C++ code to Manaserv, the new server for The Mana World, so I even used it in almost the same context. But in hindsight I consider it a bad decision to use C++ for Manaserv. Sure, it's lightning fast and allows to minimize memory usage, but it has a lot of problems. Its very slim standard library means that you need many 3rd party libraries when you want to do anything useful. And 3rd party libraries cause all kinds of problems. Licensing issues, unstable APIs, inconsistent programming styles, unsupported platforms etc.. Its pointer semantics are a source of hard to find bugs. And its lack of garbage collection means that you spend a lot of time with thinking about how to avoid memory leaks instead of solving your problem.
So it boiled down to C# vs. Java.
Anyone who has used these two language and has also experience with other programming languages will notice that these languages are very similar. Both are almost fanatically object-oriented, both compile to bytecode, both have a very comprehensive standard library and the syntax is so similar that it's faster to list the differences than what they have in common. The only two reasons for me to choose Java over C# are these:
When it comes to server programming, you shouldn't take chances. A game server must be secure, because it's an exposed target for hackers. A game server must be stable, because downtimes will annoy everyone. A game server must not have bugs, because a glitch can cause irreversible damage to the gameplay and not just a mild annoyance. And a game server must require few resources, because I will be paying for the hardware. To ensure this, I want to do this in a programming language which fulfills these criteria:
- Object-orientation
- Static typing
- Runs on Windows and Linux
- Not interpreted
- I have prior experience with it
Out of these languages, C++ is the one I have most experienced with. I contributed a lot of C++ code to Manaserv, the new server for The Mana World, so I even used it in almost the same context. But in hindsight I consider it a bad decision to use C++ for Manaserv. Sure, it's lightning fast and allows to minimize memory usage, but it has a lot of problems. Its very slim standard library means that you need many 3rd party libraries when you want to do anything useful. And 3rd party libraries cause all kinds of problems. Licensing issues, unstable APIs, inconsistent programming styles, unsupported platforms etc.. Its pointer semantics are a source of hard to find bugs. And its lack of garbage collection means that you spend a lot of time with thinking about how to avoid memory leaks instead of solving your problem.
So it boiled down to C# vs. Java.
Anyone who has used these two language and has also experience with other programming languages will notice that these languages are very similar. Both are almost fanatically object-oriented, both compile to bytecode, both have a very comprehensive standard library and the syntax is so similar that it's faster to list the differences than what they have in common. The only two reasons for me to choose Java over C# are these:
- I got a tad more experience with Java
- I don't trust Mono to run my code on Linux as well as the .NET framework does.
Wednesday, October 5, 2011
First screenshot of the graphic engine
This is a screenshot of the client after two days. As you can see I've chosen to create an isometric 2d engine for it. I decided to go for isometric, because I believe that you can create much nicer looking games with less work-hours doing pixel art.
Features so far:
Creating this took about two afternoons plus an additional day for performance tuning. The experience I collected while participating in the development of the graphic engine of the open source game The Mana World helped me a lot.
The framerate could be a lot better, though. The 18 FPS you see above are inside my virtual Windows 8 machine on my desktop PC. It runs slightly faster (>20FPS) on my netbook which runs Windows 8 natively. 20 fps is just enough for a 2d game, but considering that I want to add additional features to the engine which will suck some additional CPU cycles, this could be a problem. I hope Microsoft will improve the performance of its HTML5 canvas implementation before the Windows 8 release. Otherwise I will have to find some optimization tricks or use some shortcuts.
The last resort would be to port the whole engine to C++ and Direct2d. The metro interface can do that, but the programming style examples I've seen for this looked very old-fashioned. Win32 API old-fashioned to be exact. Unless doing this project turns out to be completely impossible in Javascript due to performance reasons I would prefer to avoid this, because it would mean that I will waste a lot of time with boring low-level stuff like hunting dangling pointers and memory leaks.
Features so far:
- Smooth scrolling
- Variable tile size
- Moving objects (no collision or path-finding yet)
- Input through hover-sensitive on-screen controller
- Detecting mouse clicks on the correct tile
Creating this took about two afternoons plus an additional day for performance tuning. The experience I collected while participating in the development of the graphic engine of the open source game The Mana World helped me a lot.
The framerate could be a lot better, though. The 18 FPS you see above are inside my virtual Windows 8 machine on my desktop PC. It runs slightly faster (>20FPS) on my netbook which runs Windows 8 natively. 20 fps is just enough for a 2d game, but considering that I want to add additional features to the engine which will suck some additional CPU cycles, this could be a problem. I hope Microsoft will improve the performance of its HTML5 canvas implementation before the Windows 8 release. Otherwise I will have to find some optimization tricks or use some shortcuts.
The last resort would be to port the whole engine to C++ and Direct2d. The metro interface can do that, but the programming style examples I've seen for this looked very old-fashioned. Win32 API old-fashioned to be exact. Unless doing this project turns out to be completely impossible in Javascript due to performance reasons I would prefer to avoid this, because it would mean that I will waste a lot of time with boring low-level stuff like hunting dangling pointers and memory leaks.
First Post
I've created this blog as a diary to document my progress while developing my own MMORPG based on the new Windows 8 metro interface.
This project is mainly a learning exercise for me to learn some new skills, namely Windows 8 Metro-style apps, Javascript, NoSQL databases and multi-threaded Java server programming.
I don't plan to go public yet. This blog is mainly to motivate myself to keep working. Maybe this project will indeed be successful. Then this blog will be an insightful documentation of the development history. Maybe it won't. We will see.
This project is mainly a learning exercise for me to learn some new skills, namely Windows 8 Metro-style apps, Javascript, NoSQL databases and multi-threaded Java server programming.
I don't plan to go public yet. This blog is mainly to motivate myself to keep working. Maybe this project will indeed be successful. Then this blog will be an insightful documentation of the development history. Maybe it won't. We will see.
Subscribe to:
Posts (Atom)