HeroEngine Forums
Welcome, Guest. Please login or Register for HeroCloud Account.

Author Topic: Syncing client prototype with server equivalent  (Read 1039 times)

FI-ScottZ

  • General Accounts
  • *
  • Posts: 1407
    • View Profile
    • Forever Interactive, Inc.
Syncing client prototype with server equivalent
« on: Apr 10, 13, 05:15:39 PM »

This is maybe not so much a tutorial but some code snippets.

The issue:
Prototypes are useful for storing named constants used in code due the ease with which they can be accessed via the $PROTONAME convention.  Often times, you may wish to use the same constant on both the server and client side, but manually updating both can be a chore.

Proposal:
Find a more automatic way to synchronize values in matching prototypes between server and client side so that one need only update the server side.



I explored this in the past.  You can use a combination of two external functions on the client side: UnmarshalPrototype() and SendPersistedClientCLI(), along with the server function MarshalPrototype() and script XMLParser.

Basically, you have two parts to update: the local copy of the prototype (the thing that would be changed if you used the CLI command /mp) and the  persisted version (modified via |mp).  You could only do the persisted one if you like, but then you would not see those changes until your local version was recreated.

SERVER FUNCTION:
(NOTE: In the last line where I called the client, VOZ is just the name of the script I had the function in.)
Code: (hsl) [Select]
//Mostly useful:
//This affects both the local client copy for as long as they are logged in,
//and the changes do persist to other sessions or to other clients,
//i.e. this is like setting values via both:
//  /mp
//AND:
//  |mp
//
//Both server and client side must have the same prototype name defined
//and the underlying classes must have matching fields.
//
//***HOWEVER***
//not all fields work in SendPersistedClientCLI, such as lists,
//lookuplists, and some other complex types.
function SyncClientProtoToServer(protoName as String, playerID as ID)
  var proto = GetPrototype(protoName)
  if proto != None
    //Get the marshalled prototype for changing the local copy:
    var marshalProto = MarshalPrototype(proto)
   
    //Get the master list of field names and values for changing the persisted copy:
    var fields = XMLParser:Parse(marshalProto)
    var masterFieldList = XMLParser:DeepParse(fields["value"])

    call client playerID VOZ:DuplicateProtoFields(protoName, marshalProto, masterFieldList)
  .
.

CLIENT FUNCTION:
Code: (hsl) [Select]
remote function DuplicateProtoFields(protoName as String, marshProto as String, masterFieldList as List of LookupList indexed by String of String)
  //for the local temporary copy; UnmarshalPrototype is like using /mp
  proto as NodeRef = GetPrototype(protoName)
  UnmarshalPrototype(marshProto, proto, true)
 
  //for the permanent copy; SendPersistedClientCLI is like using |mp (but "|" is NOT to be used in this string)
  foreach field in masterFieldList
    var str = "mp "+protoName+";"+field["name"]+"="+field["value"]
    println(str) //echo the command to the Console for observation
    SendPersistedClientCLI(str)
  .
.


USAGE:
To make it easy to use, I created a server chat command "voz" so I can enter into chat lines such as:
/voz syncproto myProtoName
where that command calls the function and syncs the prototype on my client.

NOTICE: The documentation comment before the server version states that this only works well for stanard one-dimensional fields, such as string, integer and boolean.  I had trouble making it work for more complex fields like list or class.  Even when I tried to build commands for SendPersistedClientCLI() using "mlp" I had problems that I didn't bother taking the time to try to resolve.

But if these are for basic named constants, such as numeric limits, this may suffice.

More specialized functions can be written to deal with lists and that will follow.
« Last Edit: Apr 10, 13, 05:40:57 PM by ScottZarnke »
Logged
Scott Zarnke
Lead Programmer, Visions of Zosimos
CTO, Forever Interactive, Inc.

FI-ScottZ

  • General Accounts
  • *
  • Posts: 1407
    • View Profile
    • Forever Interactive, Inc.
Re: Syncing client prototype with server equivalent
« Reply #1 on: Apr 10, 13, 05:34:21 PM »

Here is a set of functions that can be used to sync the value of one field which is of type LookupList indexed by Integer of String.  This is kind of a hack to be able to handle specific lookuplists.  You can see how you could vary this to handle other field types.

SERVER FUNCTION:
Code: [Select]
//Used to sync client's one field of a prototype whose field type is LookupList indexed by Integer of String.
//Both server and client side must have the same prototype name defined and the underlying classes
//must have the same field as named in this function.
function SyncClientProtoLLISFieldToServer(protoName as String, playerID as ID, fieldName as String)
  var proto = GetPrototype(protoName)
  if proto != None
    field as LookupList indexed by Integer of String = proto.fieldCollection[fieldName]
    call client playerID VOZ:DuplicateProtoLookupListISField(protoName, fieldName, field)
    println("SyncClientProtoLLIS Sent "+field.length+" values.")
  .
.

CLIENT FUNCTION:
Code: [Select]
remote function DuplicateProtoLookupListISField(protoName as String, fieldName as String, field as LookupList indexed by Integer of String)
  //for the local temporary copy; like using /mp
  proto as NodeRef = GetPrototype(protoName)
  proto.fieldCollection[fieldName] = field
 
  //for the permanent copy; SendPersistedClientCLI is like using |mp (but "|" not used in this string)
  foreach key in field
    var str = "mp "+protoName+";"+fieldName+"["+key+"]=$Q"+field[key]+"$Q"
    println(str) //echo the command to the Console for observation
    SendPersistedClientCLI(str)
  .
.
« Last Edit: Apr 10, 13, 05:44:43 PM by ScottZarnke »
Logged
Scott Zarnke
Lead Programmer, Visions of Zosimos
CTO, Forever Interactive, Inc.