Sign in to follow this  
Followers 0
  • entries
    2
  • comments
    8
  • views
    90

About this blog

Game Dev Blog of World Factions

Entries in this blog

martyj

In my day job I get a lot of experience with different technologies. Mostly related to mobile or website, but there is a lot of bleed over in the game industry. I've done a few Ruby on Rails applications, it's not my primary toolbox, but it has some benefits. Mostly the ease and speed of development.

 

I switched to using Ruby on Rails for the data management layer of my game. At the moment there isn't much data, only about 300 rows in a database for everything in the game. This is expected to more than triple within the next few months as I add achievements, more items, 7 new quests, and shops!

 

Eventually this will go extremely large and hard coding all that data will become very error prone and next to impossible to manage.

 

Ruby on Rails has the ability to have command line tasks which can be used to generate data.

 

Sample task:

namespace :codegen do
task :achievement_types => :environment do
 erb = ERB.new(File.read('lib/tasks/codegen/achievement_types.erb'))

 params = binding
 params.local_variable_set(:achievementTypes, AchievementType.all)

 data = erb.result(params)

 f = File.new('codegen/AchievementType.h', 'w')
 f.write(data)
 f.close()
end
end

 

What this code does is it loads an ERB file which is a templating file for Ruby.

Queries the database for all AchievementType objects,

Then creates a local binding scope for the template,

Renders the template to a string

Presto, Generated C++.

 

Erb file in question:

#ifndef ACHIEVEMENT_TYPE_H
#define ACHIEVEMENT_TYPE_H
<% achievementTypes.each do |at| %>
#define <%= at.macroname %> <%= at.id %>
<% end %>
#endif

 

Code generated:

#ifndef ACHIEVEMENT_TYPE_H
#define ACHIEVEMENT_TYPE_H

#define ACHIEVEMENT_TYPE_WOODCUTTING 1
#define ACHIEVEMENT_TYPE_FISHING 2
#define ACHIEVEMENT_TYPE_MINING 3
#define ACHIEVEMENT_TYPE_FIREMAKING 4

#endif

 

The use case above is fairly simple, but more complex situations can occur, such as Npc Conversations. Currently my NpcConversation_Gen.h file is 500 lines long. with lists of data similar to this:

 

new NpcConversation(34, 0, "69488b5b-cfd1-4255-bd74-a6b7eeb0e939",
{
 new NpcConversationAction(27, "AddInventoryItem", 39, 1),
 new NpcConversationAction(28, "CompleteMilestone", 3, 15),
},
{
 new NpcConversationConditional(12, "StartedQuest", 3, 0),
 new NpcConversationConditional(13, "HasItem", 57, 10),
}
),

 

Maintaining that code by hand would triple the amount of time to create quests.

 

So if your game uses a large amount of data, I really recommend using a web framework (Ruby on Rails, Codeigniter, Cakephp, Revel, Django, Spring, ect) to manage all your game data!

martyj

Unified Network Code

I though it would be best to share a lesson that I've learned while working on the networking aspect of my game.

 

In my game I use C sockets for network code. The server side is written in Golang. With game networking, you can't really use JSON as a data layer as the performance hits for serializing and deserializing data are too big. So the best option is to write out your data in a "file" struct format.

 

My game has a few types of structs.

 

PacketHeader, PlayerPosition, PlayerInit, PlayerQuit, PlayerMessage, ItemPickup, ItemDrop, StatusEffect.

 

I ran into a problem with writing code to handle players dropping items across all clients.

 

Sample Struct:

struct ItemDrop
{
unsigned short itemId;
float positionX;
float positionY;
float positionZ;
}

 

On the Golang side the struct was something like this:

type ItemDrop struct {
ItemId uint16
X	 float32
Y	 float32
Z    float32
}

 

They look exactly alike!

 

In C++, sizeof(ItemDrop) => 16.

 

In Golang, unsafe.Sizeof(ItemDrop) => 16

 

Everything should be perfect right? No. Notice how there are 3 float32s giving us 12 bytes, a uint16 should only be two bytes. Yet it takes up 32 bits of data.

 

On network serializing: binary.Size(ItemDrop) => 14.

 

This means that when the C++ side sends 16 bytes, golang reads 14 bytes, then re-transmits as 14 bytes. There are two problems that occurs. When a client is expecting to read a packet size of 16, it blocks on the read when Golang only sends 14 bytes. This also means that when Golang reads 14 bytes instead of the full 16, it corrupts future packets from that buffer by shifting the data 2 bytes.

 

So why Golang for the server code? Easier to write. C/C++ networking is a pain. Golang has the native support that C/C++ has, with nice modern libraries built out for it.

 

Because of situations like this, I will be re-writing the network code for client and server to use golang.

Sign in to follow this  
Followers 0