I'm trying to write a Minecraft server in Ruby as an educational project and I've hit the point now where I need to decide on how I am going to structure the in-game objects (entities). In my mind, an Entity is an in-game thing such as a block, or a player, or a skeleton.
The reason this is code review, and not game design, is because I am interested in what you think of the design from a general programming perspective, as this is an open source project and I want it to be as programmer friendly as possible.
When I started writing the current spike, I came up with a few methods I could use for entity structure.
Standard Object-Orientated Programming Inheritance
In this example, I would have used the standard OOP class hierarchy to design my entities. you would have a base class Entity
which would be extended by Player
, Sign
, Skeleton
, you get the idea. The upside of this is that it's simple and fast to come up with - until you start wanting to share functionality in two derivatives. Ruby does not have multiple inheritance, so it gets messy when I want an Enderdragon
entity to share some functionality with a Sheep
. It also limits functionality unless you do a lot of code duplication at runtime if you want to, say, make a Villager
controllable by a Player
.
Modules and Inheritance
This approach would use modules to share functionality and have it all defined at compile-time. We would have modules such as Positionable
, Orirentable
, Nameable
and a base Entity
class would define all of these at compile time in derivatives (for want of a better word - no compiling in Ruby). The advantage of this is that it's more 'clear' to other programmers. Player
would have Positionable
and RemoteControllable
whereas a Sheep
might have Positionable
. These subclasses are all designed at compile time.
Modules and Meta-Programming
Finally, this one would utilize the Factory (or Builder) pattern to have a similar approach to Modules and Inheritance
, except this time, it would define the mixins at runtime. Instances of Entity
would be re-opened at runtime and have the appropriate modules mixed-in in their factory. This has the benefit of huge flexibility at the cost of maybe perhaps slightly violating POLA.
I'm currently siding on the side of meta-programming. What do you think?
module Entity
# The Id of the Entity
attr_accessor :id
def to_s
"Entity <ID:#{id}>"
end
end
module Positionable
attr_accessor :x, :y, :z
end
module Orientable
attr_accessor :yaw, :pitch
end
module Nameable
attr_accessor :name
end
entity = Entity.new
entity.extend(Positionable)
entity.extend(Nameable)
entity.extend(Orientable)
entity.x # => 0f