Hi I’m doing a multiuser game, and starting the synchronization of the users part. At the server side I have an servlet to login and a servlet to sync that receive the current user location and respond with a list of current users position.
For now I didn’t put in place the interpolation of the characters posistions, but its working fine to synchronize multiple users positions.
The problem I’m facing is that after some minutes, sometimes seconds the client just crashes.
Do I need to release anything that I’m missing ?
here is the code that I’m using
any help will be apreciated.
var speed: int = 5;
var syncInterval:float = 0.20;
var nextSync: float = 0;
var userObjects: Array = new Array();
var otherUserType: Transform;
var activeUsers: Array = new Array();
var inUse: boolean;
// local movement direction
var dx:float = 0;
var dy:float = 0;
var dz:float = 0;
// constansts of the data array position
var POS_NAME: int = 0;
var POS_X: int = 1;
var POS_Y: int = 2;
var POS_Z: int = 3;
function Update(){
dx = Input.GetAxis("Horizontal") * speed * Time.deltaTime;
dy = 0;
dz = Input.GetAxis("Vertical") * speed * Time.deltaTime;
transform.Translate(dx , dy, dz );
sync(dx,dy,dz);
}
function sync(dx,dy,dz){
if(MainScript.userName != "" Time.time > nextSync ! inUse){
inUse = true;
var start = Time.time;
// build the form to make the call
var form = new WWWForm();
form.AddField("name", MainScript.userName);
form.AddField("x", transform.position.x.ToString() );
form.AddField("y", transform.position.y.ToString() );
form.AddField("z", transform.position.z.ToString() );
// make the remote call
var url = MainScript.server + "/sync";
var localWWW = new WWW(url, form);
yield localWWW;
form = null;
activeUsers = new Array();
// parse the result.
var users = localWWW.text.Split(";"[0]);
for(i=0; i < users.length; i++){
var oneUser = users[i].Split(","[0]);
// keep this list to check if the existing users are all active
activeUsers.Push(oneUser[POS_NAME]);
var found: boolean = false;
if( oneUser[POS_NAME] == MainScript.userName ){
found = true;
}
// update each user with the new data
for(n=0; n < userObjects.length !found; n++){
if( userObjects[n].GetComponent(UserInformationScript).userName == oneUser[POS_NAME] ){
userObjects[n].position.x = float.Parse(oneUser[POS_X]);
userObjects[n].position.y = float.Parse(oneUser[POS_Y]);
userObjects[n].position.z = float.Parse(oneUser[POS_Z]);
found = true;
break;
}
}
// add the new object if its not found
if(!found){
var pos: Vector3 = new Vector3(float.Parse(oneUser[POS_X]), float.Parse(oneUser[POS_Y]), float.Parse(oneUser[POS_Z]) );
var temp = Instantiate(otherUserType,pos,Quaternion.identity);
temp.GetComponent(UserInformationScript).userName = oneUser[POS_NAME];
userObjects.Push(temp);
}
}
// check if the existing user objects are all active.
for(n=0; n < userObjects.length; n++){
found = false;
for(m=0; m < activeUsers.length; m++){
if( userObjects[n].GetComponent(UserInformationScript).userName == activeUsers[m] ){
found = true;
break;
}
}
if(!found){
userObjects.Remove(n);
n--;
}
}
// release the WWW object
localWWW.Dispose();
localWWW = null;
// reset the next sync time
nextSync = Time.time + syncInterval;
var elapsed: float = Time.time - start;
MainScript.status = "elapsed = " + elapsed;
inUse = false;
}
}
made a simple change and its under test now.
moved the declaration of the WWW object to outside the update method, and removed the Dispose() call.
so this on the first line of the script
var localWWW: WWW;
this in the loca creation of the WWW object
// var localWWW = new WWW(url, form);
localWWW = new WWW(url, form);
yield localWWW;
and this at the end
// release the WWW object
// localWWW.Dispose();
localWWW = null;
the test is running for 5 minutes now …
oh well still crashing here is the crash report … I believe that is something related to the network …
any suggestions ??
Date/Time: 2010-12-30 20:12:02 -0200
OS Version: 10.6.5 (Build 10H574)
Architecture: x86_64
Report Version: 6
Command: unity client
Path: /Users/hamiltonlima/Documents/ludumdare19/unity client/build/mac/hideandseek-test3.app/Contents/MacOS/unity client
Version: Unity Player version 3.1.0f4 (3.1.0f4)
Parent: launchd [266]
PID: 46764
Event: hang
Duration: 7.72s (sampling started after 2 seconds)
Steps: 57 (100ms sampling interval)
Pageins: 3
Pageouts: 0
Process: unity client [46764]
Path: /Users/hamiltonlima/Documents/ludumdare19/unity client/build/mac/hideandseek-test3.app/Contents/MacOS/unity client
UID: 501
Process: AirPort Base Station Agent [299]
Path: /System/Library/CoreServices/AirPort Base Station Agent.app/Contents/MacOS/AirPort Base Station Agent
UID: 501
Thread 9cf13d4
User stack:
57 ??? (in AirPort Base Station Agent + 5692) [0x10000163c]
57 NSApplicationMain + 364 (in AppKit) [0x7fff8561d1a8]
57 -[NSApplication run] + 395 (in AppKit) [0x7fff8562448b]
57 -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 155 (in AppKit) [0x7fff8565e7a9]
57 _DPSNextEvent + 718 (in AppKit) [0x7fff8565ee64]
57 BlockUntilNextEventMatchingListInMode + 59 (in HIToolbox) [0x7fff82e0a5d8]
57 ReceiveNextEventCommon + 310 (in HIToolbox) [0x7fff82e0a71f]
57 RunCurrentEventLoopInMode + 333 (in HIToolbox) [0x7fff82e0a91a]
57 CFRunLoopRunSpecific + 575 (in CoreFoundation) [0x7fff8084cdbf]
57 __CFRunLoopRun + 1698 (in CoreFoundation) [0x7fff8084d932]
57 mach_msg_trap + 10 (in libSystem.B.dylib) [0x7fff8224c2da]
Kernel stack:
57 ipc_mqueue_receive_continue + 0 [0x210ce6]
Thread 9dbab7c DispatchQueue 2
User stack:
57 start_wqthread + 13 (in libSystem.B.dylib) [0x7fff82266005]
57 _pthread_wqthread + 353 (in libSystem.B.dylib) [0x7fff82266168]
57 _dispatch_worker_thread2 + 252 (in libSystem.B.dylib) [0x7fff8226683e]
57 _dispatch_queue_invoke + 185 (in libSystem.B.dylib) [0x7fff82266d14]
57 kevent + 10 (in libSystem.B.dylib) [0x7fff8226516a]
Kernel stack:
57 kevent + 97 [0x473932]
Thread 9dba3d4
User stack:
57 thread_start + 13 (in libSystem.B.dylib) [0x7fff822853e9]
57 _pthread_start + 331 (in libSystem.B.dylib) [0x7fff82285536]
57 ??? (in AirPort Base Station Agent + 117988) [0x10001cce4]
57 ??? (in AirPort Base Station Agent + 106856) [0x10001a168]
57 ??? (in AirPort Base Station Agent + 118817) [0x10001d021]
57 __semwait_signal + 10 (in libSystem.B.dylib) [0x7fff82286fca]
Kernel stack:
57 semaphore_wait_continue + 0 [0x22a3dd]
** the full crash report is attached.
462510–16240–$athanazio WWW crash report 2010-dec-30.txt.zip (28 KB)
doing some more investigation, when running one client the number of network connections on TIME_WAIT go to 140
looks like there is a lot of left behind WWW connections hehehe.
and more around 140 for each client … will check if I can make this WWW connection reusable.
The code you have there is extremely bad
You call the WWW each frame, thats deadly basically, at worst you get even struck by thread context errors (not on osx but windows clients and webclients on windows won’t go anywhere anymore)
Also your webserver will suffer like hell.
you need to add a condition on when its meant to check that and call the whole sync function there, like 10 times a second, once a second, after each turn or whatever depending on the game type
ok first thanks for ur motivational comment 
second Im doing what you said, first line in the sync() function
function sync(dx,dy,dz){
if(MainScript.userName != "" Time.time > nextSync ! inUse){
...
Time.time > nextSync to avoid the call on each update(),
and inUse to avoid a duplicate call if the time is not enough.
so I’m doing the call on each 0.2 secs, If I understand correctly the value from Time.time,
this is defined on the second line
var syncInterval:float = 0.20;
beside this details, any other extremelly bad part in the code ?
thanks.
oh must have missed that. I normally put these kind of checks in the place where the function is called because going into a coroutine (thats what you have there, although UnityScript unhappily stealths this kind of stuff) isn’t free.
Otherwise I don’t see anything extremely bad as you have the inUse variable although I would consider updating time and inuse right after the yield for www, so variable cpus speeds and correspondingly variable parse times for the response don’t impact the future transmissions
Generally I would consider don’t calling this from update at all.
Instead I would make this coroutine looping for the lifetime of the “play mode” and fire the coroutine off in start already.
That frees you from the presence of update.
Also the update code there is a bit troublesome: You actually sync the current delta of this frame, but you don’t sync each frame. that won’t work out technically like this as the representation on the server will be completely wrong basically.
What you would need is having the dx, dy, dz on the monobehaviour itself and sum it up on each change and then transmit that sum up to now so the state is correct and resetting it (ie setting the cached dx, dz states to 0) after fetching it for the WWW transmit in the WWWForm
alternatively don’t send the dx and instead send the current position at the time of sync. that gives the most accurate representation. (as you use WWW you don’t do any autorative handling on the server anyway so dx, dz won’t serve any verification purpose and sending the actual position is fine)
Thanks for the comments !
The problem about the WWW seems to be the nature of atomic network connection, that starts and closes right after, this seems to be using too many network resources on the machine, now Im changing to use TCPClient, and looking for a way to make the calls asynchronous.
as soon I have the code will post here.
WWW is async actually (thats why you can / must yield)
But whats not async and what really is eating the hair from your head is the string parsing.
Thats why such data normally is sent as XML or JSON so you can use those parsers which are significantly more optimized than the bruteforce parser you have there
TCPClient won’t get much from it as having multiple threads will only make code 10 times more complex, as callbacks / threads can not touch any class from the UnityEngine framework or you will get a bombing up application
As for the connection terminating: that makes sense in this case as WWW is not intend for continous data transfer, http itself isn’t really optimal for it. Thats why there is TCP sockets, UDP sockets and unity networking to communicate to a realtime server that does not die a hefty death in hell if you do something like this here like a webserver will if you have the hope to do permanent connections with several hundred users against a webserver.
Love this !! LOL
thanks again, and for the sake of documentation the version with sockets is running from now … started 3 clients and will go to sleep 
I hope to have good news tomorrow morning.
cheers
Looking forward to it 
And glad the total messup in that sentence still made sense … too late combined with too long sentence resulted in the expected “messy messy d’oh” 
Yes ! while I was sleeping the crash test went ok ! 6 hours with 3 clients running on the same machine of the server
and the clients didnt crash !!
here is the code that I’m using for the socket calls
// code adapted from
// http://answers.unity3d.com/questions/8128/unity-project-and-3rd-party-apps/8183#8183
//
import System;
import System.IO;
import System.Net.Sockets;
static var socketReady:boolean = false;
static var mySocket:TcpClient;
static var theStream: NetworkStream;
static var theWriter: StreamWriter;
static var theReader: StreamReader;
static var Host:String = "localhost";
static var Port:int = 42000;
static var response:String;
static function setupSocket() {
try {
mySocket = new TcpClient(Host, Port);
theStream = mySocket.GetStream();
theWriter = new StreamWriter(theStream);
theReader = new StreamReader(theStream);
socketReady = true;
}
catch (e:Exception) {
Debug.Log("Socket error: " + e);
}
return socketReady;
}
static function writeSocket(line:String) {
if (!socketReady)
return;
var foo:String = line + "\n";
theWriter.Write(foo);
theWriter.Flush();
}
static function readSocket() {
if (!socketReady){
response = "";
} else {
//if (theStream.DataAvailable)
response = theReader.ReadLine();
}
return response;
}
static function closeSocket() {
if (!socketReady)
return;
theWriter.Close();
theReader.Close();
mySocket.Close();
socketReady = false;
}
and the calls are something like this
var data:String = "SYNC:"+ MainScript.userName +",";
data += transform.position.x.ToString()+",";
data += transform.position.y.ToString()+",";
data += transform.position.z.ToString();
TCPScript.writeSocket(data);
yield TCPScript.readSocket();
var users = TCPScript.response.Split(";"[0]);
Now I’m facing other problem
when the use is not in the list of existing ones anymore … but this should be discussed in other thread I think.
cheers and thanks for the comments !
1 Like