perlquestion
wanna_code_perl
<p>Hello Monks,</p>
<p>I'm toying with some proof-of-concept code for an online game with the logic written in Perl (and Moose). The game would have a typical client/server architecture, with the closed-source server determining all critical game state and logic, and open-source client handling the player UI and game view. What I'm not sure of is how to model the game state in the client without duplication of code. Say I have the bad guys modeled like so (simplified):</p>
<code>
package TheGame::BadGuy;
use Moose;
has health => (is => 'rw', isa => 'Num'); # 0 == dead
has max_health=> (is => 'rw', isa => 'Num'); # 100% health
has inventory => (is => 'rw',
isa => 'ArrayRef[TheGame::Item]');
sub health_percent {
return $_[0]->health / $_[0]->max_health;
}
# ... many more attributes and methods
</code>
<p>The server of course needs low-level r/w access to this information. The client, however, will run on the player's computer and must therefore have limited access but enough to display the bad guys on screen and determine how the player may interact with them.</p>
<p>The rub is: I'm operating under the assumption that someone <b>will</b> start hacking at the Perl client code. Obviously the client can't just do <c>$badguy->health(0);</c> and expect a meaningful result; that's not the issue. The issue is exposing only select fields to the client (possibly munged) so it's non-trivial to reverse-engineer the game mechanics (see Note), and how to design the class(es) efficiently. For example:</p>
<readmore>
<ul>
<li>The client shouldn't know the exact health amount of the bad guy, but the client needs the percentage to show a health bar.</li>
<li>The client shouldn't know anything about the bad guy's inventory at all.</li>
</ul>
<p>So, then, what's the best way to go about this? Keeping in mind the client will receive some kind of serialized copy of the relevant data (and only the relevant data), here are some options I've brainstormed so far:</p>
<ol>
<li>Subclass to <c>TheGame::BadGuy::Client</c>, with the "hidden" fields overridden to throw exceptions (the underlying hash elements wouldn't ever have been initialized, either). This sounds good on the surface, but it also implies I have to either release the server <c>TheGame::BadGuy</c> source with the client, or create an alternate <c>TheGame::BadGuy</c> package, which makes me more than a little queasy.</li>
<li>Don't bother subclassing anything. Just instantiate a <c>TheGame::BadGuy</c> in the client, with only the desired fields populated. Allow <c>undef</c> for others. Override <c>health_percentage</c>, or maybe just normalize <c>health</c> and <c>max_health</c> to e.g. 0..1 or 0..100.</li>
<li>Create a new non-subclassed <c>TheGame::Client::BadGuy</c> class that mirrors the desired fields, with no mention of any other fields.</li>
<li>Go up one more level: <c>TheGame::BadGuy</c> is the superclass for <c>TheGame::BadGuy::Server</c> and <c>TheGame::BadGuy::Client</c></li>
<li>Something else?</li>
</ol>
<p>There are tradeoffs to each of these. But it still feels like there's a pattern I'm missing (it's been a while since I did any serious OO design, plus this is my first non-trivial effort with Moose).</p>
<p><i>Note: I believe that determined players <b>should</b> be able to figure out most of the game mechanics, but doing so should involve observation, math, and intuition, not just a couple of trivial method calls. In fact I'd be honored if someone cared enough to go to the effort.</i></p>