Mythruna
January 19, 2019, 01:09:57 AM *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
News: Welcome to the new forums. See "Announcements" for a note for new users.
 
   Home   Help Search Login Register  
Pages: [1]
  Print  
Author Topic: Inch by inch...  (Read 5759 times)
pspeed
Administrator
Hero Member
*****
Posts: 5557



View Profile
« on: February 15, 2015, 01:12:14 AM »

Last weekend I worked a little more on character creation.  You wouldn't think this was super important at this stage but it's solving a lot of network problems in advance of when they become super big issues.  I'm also making sure I don't paint myself into any corners.

So, I kind of worked out how the characters are managed internally and needed to start working on the actual edit fields and stuff.  It occurred to me that the object scripting system I had was pretty general so I looked into whether I could make a "character" type that had actions configured for things like setting the name, race, etc..  It's easier if I can tap into this rather than implementing different network commands for all of it.  It also means it automatically transfers from multiplayer to single player.  Win/win.

It turns out, not only was I really flexible and forward-thinking when I implemented the object action scripting system, I'd actually already used it to implement player character behavior.  It's what is used in the inventory menu to set what is in the right/left hand, moving inventory items around, and so on. 

For example, I have scripts like this:
Code:
def characterType = objectTemplate( "Character" ) {
 
    action("unequip") { EquipmentSlot slot ->
        def held = getHeldItems(self, slot);
        boolean unequipped = true;
        held.each{ unequipped = unequipped && it.execute("Unequip") }       
        return unequipped;
    }

    action("unequipRight") {
        return self.unequip(EquipmentSlot.RightHand);
    }

    action("unequipLeft") {
        return self.unequip(EquipmentSlot.LeftHand);
    }
 
    action("equipRight") { EntityId item ->
        self.equip(item, EquipmentSlot.RightHand);
    }
   
    action("equipLeft") { item ->
        self.equip(item, EquipmentSlot.LeftHand);
    }

    action("equip") { EntityId item, EquipmentSlot slot ->
    ....

Which means on the UI side I can do things like ActionService.execute(player, "equipRight", swordEntity) and the script in the back-end game logic is run as defined above.  Because the player character entity is already of type "Character" and so has these scripts automatically available.

During character creation, things are a little different because you don't want most of that available but you do (temporarily) want a bunch of other stuff available... like being able to set race, stats, name, etc... all of which you'd want turned off during the actual game.

Past-me was smart and let the type of object be changed on the fly.  So it could start as a bunch of "New Character" actions and later by switched to regular "Character" to play the game.  Sweet.

Player-characters (PCs) will have first name, last name, etc. that will also be combined into the standard entity "name" for all other purposes.  I thought this was a good place to start since the setting of the actual entity state should happen in the "back end" and setting either first name or last name should reset the composite "name".  So I implemented some sample actions just to see.  For some debugging/testing:

Code:
def workingType = objectTemplate( "NewCharacter" ) {
 
    action("setFirstName") { String part ->       
        println self.toString() + "->setFirstName(" + part + ")"
    }
   
    action("setLastName") { String part ->       
        println self.toString() + "->setLastName(" + part + ")"
    }
   
    action("setGender") { Gender gender ->
        self << gender
    }
}

Then I ran it...

...

...Kaboom.

I hadn't implemented networking of the action service yet, of course.  Duh.  I'd forgotten because normal entity interactions have been networked for some time (that part is even open source).  Always something...

Networking with SpiderMonkey

In jMonkeyEngine, the networking API is called SpiderMonkey.  (I wrote this library by the way so I'm pretty familiar with it. Smiley)

SpiderMonkey (SM) is kind of bare bones and tries to be as efficient as possible.  It works by passing messages which you as a developer craft for yourself.  It then converts them to bits a fairly efficient way and handles the over-the-network transfer for you.  So, when faced with some new service that one must expose over the network, one immediately starts thinking of creating all of these message types.

...the thing is, sometimes you don't want to be purely efficient.  You'd just like to avoid writing a bunch of code.  Something like the action service is one of those things... certainly during the prototype phase.  Interaction is not constant but at specific discrete user actions. It's ok if individual calls are 12% less efficient or whatever.

RPC or "remote procedure calls" is a standard pattern for doing this sort of thing in a general way.  Java extends this idea to "Remote Method Invocation" (RMI) which is a more object-oriented friendly way of doing the same thing.  Basically, you can take an object on the server and expose it to the clients. (or vice versa).  Java's version is very heavy-weight and uses its own protocol.  It does a lot of stuff I don't need and would be hard to make play nice with SM.  SM already has a simple RMI service but it has its own issues (I didn't write it but that's not the only issue Wink) and didn't actually meet some of my needs.  So I thought to rewrite it.

In addition, SM has lacked a 'service model' for some time.  I ripped the old one out when I rewrote it because it didn't really provide any value and didn't match the new architecture very well.  Since then, I've implemented several reusable SM-based services and started to see how a service model should be implemented.

Long store short: this past week I was able to fit in an hour here and there to implement a service model that will be (soon) incorporated into SM.  On top of this I wrote a bare-bones RPC implementation.  Dirt simple "execute this procedure number on this object number with these arguments" type of stuff.  On top of _that_ I implemented a basic RMI service.  (All of those will eventually be contributed back to SM once I let them burn in a little while and shape up a bit.)

Today I finally put the final "make it work" touches on all of that and so tonight I was able to start threading it through the existing (in th new engine) Mythruna networking code.  That was tonight's job and I'm happy to say after working through various little setup bugs along the way, I was able to execute the setFirstName()/setLastName() actions on a NewCharacter object.... over the network.

Client code:
Code:
        // Just testing some stuff           
        System.out.println( "Action service:" + actions );       
        actions.execute(workingChar.getId(), "setFirstName", "Fibbity");
        actions.execute(workingChar.getId(), "setLastName", "McFopp");

On the server console, I saw:
Code:
EntityId[69]->setFirstName(Fibbity)
EntityId[69]->setLastName(McFopp)

Success.

On the one hand, it's only a small step of progress on the "feature list".  On the other hand, for every other non-performance-critical service I need to expose over the network, it's only a few line change to expose that service.  And that's a pretty big deal to me.  I'd rather spend time on the stuff that really needs the detailed per-message attention.

So I'm making progress... like a turtle.
Logged
Rayblon
Donators
Hero Member
***
Posts: 1833


One day I'll be a species in Mythruna...


View Profile
« Reply #1 on: February 15, 2015, 09:01:34 AM »

Slow and steady wins the race. Honestly, though, the way you talked about the progress made it sound like you're going at a pretty good pace with the coding.
Logged


^Useful links^
ebag51
Donators
Hero Member
***
Posts: 531


View Profile
« Reply #2 on: February 15, 2015, 12:34:42 PM »

Good job Paul. Cheesy Keep up the good work.
Logged

Rayblon
Donators
Hero Member
***
Posts: 1833


One day I'll be a species in Mythruna...


View Profile
« Reply #3 on: February 15, 2015, 01:08:47 PM »

's to you, Paul. :3

« Last Edit: February 15, 2015, 01:29:13 PM by Rayblon » Logged


^Useful links^
BenKenobiWan
Friendly Moderator
Donators
Hero Member
***
Posts: 673


Jesus loves you!


View Profile
« Reply #4 on: February 15, 2015, 08:57:03 PM »

Sweet.
Logged
Sean
Donators
Hero Member
***
Posts: 598



View Profile
« Reply #5 on: February 15, 2015, 09:34:42 PM »

Logged

"People willing to trade their freedom for temporary security deserve neither and will lose both." - Benjamin Franklin
Rayblon
Donators
Hero Member
***
Posts: 1833


One day I'll be a species in Mythruna...


View Profile
« Reply #6 on: February 15, 2015, 09:38:22 PM »



Best.

500th Post.

Ever.
Logged


^Useful links^
Conner
Donators
Newbie
***
Posts: 31


View Profile
« Reply #7 on: February 16, 2015, 09:58:51 AM »

Very nice Wink
Logged
Moonkey
Hero Member
*****
Posts: 1587

This is probably a picture.


View Profile
« Reply #8 on: February 17, 2015, 05:09:23 AM »

Heheh, good work, Paul. Smiley
Logged

Mythruna: Don't you dare read any posts I made before 2014.
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.20 | SMF © 2013, Simple Machines Valid XHTML 1.0! Valid CSS!