//
//  SystemController.m
//  SFS2X
//
//  Original development by Infosfer Game Technologies Ltd. | http://www.infosfer.com.
//
//  Maintained and developed by A51 Integrated.
//  Copyright 2012 A51 Integrated | http://a51integrated.com. All rights reserved.
//

#import "SystemController.h"
#import "SmartFox2XClient.h"
#import "BaseRequest.h"
#import "Logger.h"
#import "BitSwarmClient.h"
#import "SFSEvent.h"
#import "SFSBuddyEvent.h"
#import "LoginRequest.h"
#import "SFSUser.h"
#import "SFSBuddy.h"
#import "SFSBuddyVariable.h"
#import "SFSRoom.h"
#import "SFSErrorCodes.h"
#import "CreateRoomRequest.h"
#import "GenericMessageRequest.h"
#import "SetRoomVariablesRequest.h"
#import "ChangeRoomCapacityRequest.h"
#import "ChangeRoomPasswordStateRequest.h"
#import "SpectatorToPlayerRequest.h"
#import "PlayerToSpectatorRequest.h"
#import "InitBuddyListRequest.h"
#import "AddBuddyRequest.h"
#import "RemoveBuddyRequest.h"
#import "BlockBuddyRequest.h"
#import "BuddyOnlineState.h"
#import "GoOnlineRequest.h"
#import "ReservedBuddyVariables.h"
#import "SetBuddyVariablesRequest.h"
#import "FindRoomsRequest.h"
#import "FindUsersRequest.h"
#import "InviteUsersRequest.h"
#import "SubscribeRoomGroupRequest.h"
#import "ChangeRoomNameRequest.h"
#import "SetUserVariablesRequest.h"
#import "SetUserPositionRequest.h"
#import "SetMMOItemVariables.h"
#import "ClientDisconnectionReason.h"
#import "LogoutRequest.h"
#import "GenericMessageType.h"
#import "JoinRoomRequest.h"
#import "SFSArray.h"
#import "ArrayUtil.h"
#import "SFSRoomVariable.h"
#import "SFSUserVariable.h"
#import "SFSInvitation.h"
#import "SFSBuddyManager.h"

#import "Vec3D.h"
#import "MMORoom.h"
#import "MMOItem.h"
#import "MMOItemVariable.h"


@implementation SystemController

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"

@synthesize sfs = _sfs;
@synthesize bitSwarm = _bitSwarm;
@synthesize requestHandlers = _requestHandlers;



-(void)initRequestHandlers {
	
	[_requestHandlers setObject:@"fnHandshake:" forKey:[NSNumber numberWithInt:(int)BaseRequest_Handshake]];
	[_requestHandlers setObject:@"fnLogin:" forKey:[NSNumber numberWithInt:(int)BaseRequest_Login]];
	[_requestHandlers setObject:@"fnLogout:" forKey:[NSNumber numberWithInt:(int)BaseRequest_Logout]];
	[_requestHandlers setObject:@"fnJoinRoom:" forKey:[NSNumber numberWithInt:(int)BaseRequest_JoinRoom]];
	[_requestHandlers setObject:@"fnCreateRoom:" forKey:[NSNumber numberWithInt:(int)BaseRequest_CreateRoom]];
	[_requestHandlers setObject:@"fnGenericMessage:" forKey:[NSNumber numberWithInt:(int)BaseRequest_GenericMessage]];
	[_requestHandlers setObject:@"fnChangeRoomName:" forKey:[NSNumber numberWithInt:(int)BaseRequest_ChangeRoomName]];
	[_requestHandlers setObject:@"fnChangeRoomPassword:" forKey:[NSNumber numberWithInt:(int)BaseRequest_ChangeRoomPassword]];
	[_requestHandlers setObject:@"fnChangeRoomCapacity:" forKey:[NSNumber numberWithInt:(int)BaseRequest_ChangeRoomCapacity]];
	[_requestHandlers setObject:@"fnSendObject:" forKey:[NSNumber numberWithInt:(int)BaseRequest_ObjectMessage]];
	[_requestHandlers setObject:@"fnSetRoomVariables:" forKey:[NSNumber numberWithInt:(int)BaseRequest_SetRoomVariables]];
	[_requestHandlers setObject:@"fnSetUserVariables:" forKey:[NSNumber numberWithInt:(int)BaseRequest_SetUserVariables]];
	//[_requestHandlers setObject:@"fnCallExtension:" forKey:[NSNumber numberWithInt:(int)BaseRequest_CallExtension]];
	[_requestHandlers setObject:@"fnSubscribeRoomGroup:" forKey:[NSNumber numberWithInt:(int)BaseRequest_SubscribeRoomGroup]];
	[_requestHandlers setObject:@"fnUnsubscribeRoomGroup:" forKey:[NSNumber numberWithInt:(int)BaseRequest_UnsubscribeRoomGroup]];
	[_requestHandlers setObject:@"fnSpectatorToPlayer:" forKey:[NSNumber numberWithInt:(int)BaseRequest_SpectatorToPlayer]];
	[_requestHandlers setObject:@"fnPlayerToSpectator:" forKey:[NSNumber numberWithInt:(int)BaseRequest_PlayerToSpectator]];
	[_requestHandlers setObject:@"fnInitBuddyList:" forKey:[NSNumber numberWithInt:(int)BaseRequest_InitBuddyList]];
	[_requestHandlers setObject:@"fnAddBuddy:" forKey:[NSNumber numberWithInt:(int)BaseRequest_AddBuddy]];
	[_requestHandlers setObject:@"fnRemoveBuddy:" forKey:[NSNumber numberWithInt:(int)BaseRequest_RemoveBuddy]];
	[_requestHandlers setObject:@"fnBlockBuddy:" forKey:[NSNumber numberWithInt:(int)BaseRequest_BlockBuddy]];
	[_requestHandlers setObject:@"fnGoOnline:" forKey:[NSNumber numberWithInt:(int)BaseRequest_GoOnline]];
	[_requestHandlers setObject:@"fnSetBuddyVariables:" forKey:[NSNumber numberWithInt:(int)BaseRequest_SetBuddyVariables]];
	[_requestHandlers setObject:@"fnFindRooms:" forKey:[NSNumber numberWithInt:(int)BaseRequest_FindRooms]];
	[_requestHandlers setObject:@"fnFindUsers:" forKey:[NSNumber numberWithInt:(int)BaseRequest_FindUsers]];
	[_requestHandlers setObject:@"fnInviteUsers:" forKey:[NSNumber numberWithInt:(int)BaseRequest_InviteUser]];
	[_requestHandlers setObject:@"fnInvitationReply:" forKey:[NSNumber numberWithInt:(int)BaseRequest_InvitationReply]];
	[_requestHandlers setObject:@"fnQuickJoinGame:" forKey:[NSNumber numberWithInt:(int)BaseRequest_QuickJoinGame]];
    
    [_requestHandlers setObject:@"fnPingPong:" forKey:[NSNumber numberWithInt:(int)BaseRequest_PingPong]];
    
    [_requestHandlers setObject:@"fnSetUserPosition:" forKey:[NSNumber numberWithInt:(int)BaseRequest_SetUserPosition]];
    
	[_requestHandlers setObject:@"fnUserEnterRoom:" forKey:[NSNumber numberWithInt:1000]];
	[_requestHandlers setObject:@"fnUserCountChange:" forKey:[NSNumber numberWithInt:1001]];
	[_requestHandlers setObject:@"fnUserLost:" forKey:[NSNumber numberWithInt:1002]];
	[_requestHandlers setObject:@"fnRoomLost:" forKey:[NSNumber numberWithInt:1003]];
	[_requestHandlers setObject:@"fnUserExitRoom:" forKey:[NSNumber numberWithInt:1004]];
	[_requestHandlers setObject:@"fnClientDisconnection:" forKey:[NSNumber numberWithInt:1005]];
    [_requestHandlers setObject:@"fnReconnectionFailure:" forKey:[NSNumber numberWithInt:1006]];
    [_requestHandlers setObject:@"fnSetMMOItemVariables:" forKey:[NSNumber numberWithInt:1007]];
	
}

-(id)initWithBitSwarm:(BitSwarmClient *)bitSwarm
{
	if ((self = [super init]))
    {
		
		self.bitSwarm = bitSwarm;
		self.sfs = bitSwarm.sfs;
		
		self.requestHandlers = [NSMutableDictionary dictionary];
		
		[self initRequestHandlers];
	}
	return self;
}


-(NSString *)getEvtName:(NSInteger)id_ {
	NSString *name = [_requestHandlers objectForKey:[NSNumber numberWithInt:(int)id_]];
	
	name = [name substringFromIndex:2];
	
	return name;
}

-(void)handleMessage:(id <IMessage>)message
{
	
	if (_sfs.debug)
		[_log info:[NSString stringWithFormat:@"Handling Message: %@ %@", [self getEvtName:[message id]], message]];
		
	NSString *fnName = [_requestHandlers objectForKey:[NSNumber numberWithInt:(int)[message id]]];
	
	if (fnName != nil)
		[self performSelector:NSSelectorFromString(fnName) withObject:message];
	
	else
		[_log warn:[NSString stringWithFormat:@"Unknown message id: %ld", (long)[message id]]];
	

}

-(void)fnHandshake:(id <IMessage>)msg
{
	NSDictionary *evtParams = [NSDictionary dictionaryWithObjectsAndKeys:msg, @"message",nil];
		
	[_sfs handleHandshake:[[SFSEvent alloc] initWithType:SFSEvent_HANDSHAKE params:evtParams]];
	
}

-(void)populateRoomList:(id <ISFSArray>)roomList {
	
	id <IRoomManager> roomManager = _sfs.roomManager;
	
	for (int j = 0; j < [roomList size]; j++) {
		id <ISFSArray> roomObj = [roomList getSFSArray:j];
		id <Room> newRoom = [SFSRoom fromSFSArray:roomObj];
		
		[roomManager replaceRoom:newRoom addToGroupIfMissing:YES];
	}
}

-(void)fnLogin:(id <IMessage>)msg {
	id <ISFSObject> obj = [msg content];
	
	NSMutableDictionary *evtParams = [[NSMutableDictionary alloc] init];
	
	if ([obj isNull:BaseRequest_KEY_ERROR_CODE]) {
		
		[self populateRoomList:[obj getSFSArray:LoginRequest_KEY_ROOMLIST]];
		
		_sfs.mySelf = [[SFSUser alloc] initWithId:[obj getInt:LoginRequest_KEY_ID] name:[obj getUtfString:LoginRequest_KEY_USER_NAME] isItMe:YES];
		
		[_sfs.mySelf setUserManager:_sfs.userManager];
		[_sfs.mySelf setPrivilegeId:[obj getShort:LoginRequest_KEY_PRIVILEGE_ID]];
		[_sfs.userManager addUser:_sfs.mySelf];
		
		[_sfs setReconnectionSeconds:[obj getShort:LoginRequest_KEY_RECONNECTION_SECONDS]];
		
		[evtParams setValue:[obj getUtfString:LoginRequest_KEY_ZONE_NAME] forKey:@"zone"];
		[evtParams setValue:_sfs.mySelf forKey:@"user"];
		[evtParams setValue:[obj getSFSObject:LoginRequest_KEY_PARAMS] forKey:@"data"];
		
		SFSEvent *evt = [[SFSEvent alloc] initWithType:SFSEvent_LOGIN params:evtParams];
		
		[_sfs handleLogin:evt];
		
		if ([_sfs.delegate respondsToSelector:@selector(onLogin:)]) {
			[_sfs.delegate onLogin:evt];
		}
	}
	
	else
    {
        NSNumber *errorCode = [NSNumber numberWithInt: (int)[obj getShort:BaseRequest_KEY_ERROR_CODE]];
		NSString *errorMsg = [SFSErrorCodes getErrorMessage:errorCode params:[obj getUtfStringArray:BaseRequest_KEY_ERROR_PARAMS]];
		
		[evtParams setValue:[NSNumber numberWithBool:NO] forKey:@"success"];
		[evtParams setValue:errorMsg forKey:@"errorMessage"];
        [evtParams setValue:errorCode forKey:@"errorCode"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onLoginError:)]) {
			[_sfs.delegate onLoginError:[[SFSEvent alloc] initWithType:SFSEvent_LOGIN_ERROR params:evtParams]];
		}
	}

}

-(void)fnCreateRoom:(id <IMessage>)msg {
	id <ISFSObject> obj = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	if ([obj isNull:BaseRequest_KEY_ERROR_CODE]) {
		
		
		id <IRoomManager> roomManager = _sfs.roomManager;
		
		id <Room> newRoom = [SFSRoom fromSFSArray:[obj getSFSArray:CreateRoomRequest_KEY_ROOM]];
		[newRoom setRoomManager:_sfs.roomManager];
		
		[roomManager addRoom:newRoom addGroupIfMissing:YES];
		
		[evtParams setValue:newRoom forKey:@"room"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onRoomAdd:)]) {
			[_sfs.delegate onRoomAdd:[[SFSEvent alloc] initWithType:SFSEvent_ROOM_ADD params:evtParams]];
		}
	}
	else {
		NSString *errorMsg = [SFSErrorCodes getErrorMessage:[NSNumber numberWithShort:[obj getShort:BaseRequest_KEY_ERROR_CODE]] params:[obj getUtfStringArray:BaseRequest_KEY_ERROR_PARAMS]];
		
		[evtParams setValue:errorMsg forKey:@"errorMessage"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onRoomCreationError:)]) {
			[_sfs.delegate onRoomCreationError:[[SFSEvent alloc] initWithType:SFSEvent_ROOM_CREATION_ERROR params:evtParams]];
		}
	}
}

-(id <User>)getOrCreateUser:(id <ISFSArray>)userObj addToGlobalManager:(BOOL)addToGlobalManager room:(id <Room>)room
{
	
	int uId = (int)[userObj getInt:0];
	id <User> user = [_sfs.userManager getUserById:uId];
	
	if (user == nil)
    {
		user = [SFSUser fromSFSArray:userObj room:room];
		[user setUserManager:_sfs.userManager];
	}
    // User is already managed, make sure to update the playerId
    //
    // Since 0.9.9 (AS3):
    // Also the user could have updated his variables, so change them as well
	else if(room != nil)
    {
		[user setPlayerId:[userObj getShort:3] room:room];
        
        SFSArray *uVars = [userObj getSFSArray:4];
		NSMutableArray* vars = [NSMutableArray array];
        
		for(int i = 0; i < [uVars size]; i++)
        {
			[vars addObject: [SFSUserVariable fromSFSArray:[uVars getSFSArray:i]]];
        }
		
		/*
		 * 	{ Jul 14th 2023 }
		 *
		 *  Replace all User variables with what the server has sent us
		 *  This is done because we already have "met" this User and it's original object is cached in the UserManager
		 *  (for object reference equality purposes(*))
		 *
		 *  We trust the server to have the latest state of this User's Variables, while the client
		 *  may have an outdated view. Example: the client has left the previous Room where we met it and has now
		 *  returned, but it may have altered the Vars during that period.
		 *
		 *   (*) => object reference equality was more of a concern back in the Actionscript days where we would link
		 *   User objects to UI widgets such as a User list. Not sure how this is still relevant with Unity and other platforms.
		 *
		 *   Bottom line object reference equality allows to compare (myUserRef == userObj) to determine if it's the same thing
		 *   The obvious alternative would be to use: (myUserRef.equals(userObj))
		 *   In this case we could drop the whole reference preservation system.
		 *
		 */
		
		[ ((SFSUser*)user) replaceVariables: vars ];
    }
	
	if (addToGlobalManager)
    {
		[_sfs.userManager addUser:user];
	}
	
	return user;
}

-(void)fnJoinRoom:(id <IMessage>)msg
{
	
	id <IRoomManager> roomManager = _sfs.roomManager;
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	_sfs.isJoining = NO;
	
	if ([sfso isNull:BaseRequest_KEY_ERROR_CODE])
    {
		id <ISFSArray> roomObj = [sfso getSFSArray:JoinRoomRequest_KEY_ROOM];
		id <ISFSArray> userList = [sfso getSFSArray:JoinRoomRequest_KEY_USER_LIST];
		
		id <Room> room = [SFSRoom fromSFSArray:roomObj];
		
		[room setRoomManager:_sfs.roomManager];
        
		room = [roomManager replaceRoom:room addToGroupIfMissing:[roomManager containsGroup:[room groupId]]];
		
		for (int j = 0; j < [userList size]; j++)
        {
			id <ISFSArray> userObj = [userList getSFSArray:j];
			
			id <User> user = [self getOrCreateUser:userObj addToGlobalManager:YES room:room];
			
			[user setPlayerId:[userObj getShort:3] room:room];
			
			[room addUser:user];
		}
		
		[room setIsJoined:YES];
		
		_sfs.lastJoinedRoom = room;
		
		[evtParams setValue:room forKey:@"room"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onRoomJoin:)])
        {
			[_sfs.delegate onRoomJoin:[[SFSEvent alloc] initWithType:SFSEvent_ROOM_JOIN params:evtParams]];
		}
		
	}
	else
    {
		NSString *errorMsg = [SFSErrorCodes getErrorMessage:[NSNumber numberWithShort:[sfso getShort:BaseRequest_KEY_ERROR_CODE]] params:[sfso getUtfStringArray:BaseRequest_KEY_ERROR_PARAMS]];
		
		[evtParams setValue:errorMsg forKey:@"errorMessage"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onRoomJoinError:)])
        {
			[_sfs.delegate onRoomJoinError:[[SFSEvent alloc] initWithType:SFSEvent_ROOM_JOIN_ERROR params:evtParams]];
		}
		
	}


}

-(void)fnUserEnterRoom:(id <IMessage>)msg {
	
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	id <Room> room = [_sfs.roomManager getRoomById:[sfso getInt:@"r"]];
	
	if (room != nil) {
		
		id <ISFSArray> userObj = [sfso getSFSArray:@"u"];
		id <User> user = [self getOrCreateUser:userObj addToGlobalManager:YES room:room];
		
		[room addUser:user];
		
		if ([_sfs.delegate respondsToSelector:@selector(onUserEnterRoom:)]) {
			[evtParams setValue:user forKey:@"user"];
			[evtParams setValue:room forKey:@"room"];
			[_sfs.delegate onUserEnterRoom:[[SFSEvent alloc] initWithType:SFSEvent_USER_ENTER_ROOM params:evtParams]];
		}
		
	}
}

-(void)fnUserCountChange:(id <IMessage>)msg {
	
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	id <Room> room = [_sfs.roomManager getRoomById:[sfso getInt:@"r"]];
	
	if (room != nil) {
		
		int uCount = (int)[sfso getShort:@"uc"];
		int sCount = 0;
		
		if ([sfso containsKey:@"sc"]) {
			sCount = (int)[sfso getShort:@"sc"];
		}
		
		[room setUserCount:uCount];
		[room setSpectatorCount:sCount];
		
		
		if ([_sfs.delegate respondsToSelector:@selector(onUserCountChange:)]) {
			
			[evtParams setValue:room forKey:@"room"];
			[evtParams setValue:[NSNumber numberWithInt:uCount] forKey:@"uCount"];
			[evtParams setValue:[NSNumber numberWithInt:sCount] forKey:@"sCount"];
			
			[_sfs.delegate onUserCountChange:[[SFSEvent alloc] initWithType:SFSEvent_USER_COUNT_CHANGE params:evtParams]];
		}
	}
	
}

-(void)fnUserLost:(id <IMessage>)msg {
	
	id <ISFSObject> sfso = [msg content];
	
	int uId = (int)[sfso getInt:@"u"];
	
	id <User> user = [_sfs.userManager getUserById:uId];
    
	if (user != nil) {
        
		NSArray *joinedRooms = [_sfs.roomManager getUserRooms:user];
		
		[_sfs.roomManager removeUser:user];
		[_sfs.userManager removeUser:user];
		
		for (id <Room> room in joinedRooms) {
			
			if ([room conformsToProtocol:@protocol(Room)]) {
                
				if ([_sfs.delegate respondsToSelector:@selector(onUserExitRoom:)]) {
					
					[_sfs.delegate onUserExitRoom:[[SFSEvent alloc] initWithType:SFSEvent_USER_EXIT_ROOM params:[NSDictionary dictionaryWithObjectsAndKeys:user, @"user", room, @"room", nil]]];
				}
			}
		}
	}
}

-(void)fnRoomLost:(id <IMessage>)msg {
	
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	int rId = (int)[sfso getInt:@"r"];
	
	id <Room> room = [_sfs.roomManager getRoomById:rId];
	
	id <IUserManager> globalUserManager = _sfs.userManager;
	
	if (room != nil) {
		
		
		[_sfs.roomManager removeRoom:room];
		for (id <User> user in [room userList]) {
			if ([user conformsToProtocol:@protocol(User)]) {
				[globalUserManager removeUser:user];
			}
		}
		
		[evtParams setValue:room forKey:@"room"];
		
		
		if ([_sfs.delegate respondsToSelector:@selector(onRoomRemove:)]) {
			[_sfs.delegate onRoomRemove:[[SFSEvent alloc] initWithType:SFSEvent_ROOM_REMOVE params:evtParams]];
		}
		
		
	}

}

-(void)handlePublicMessage:(id <ISFSObject>)sfso {
	
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	int rId = (int)[sfso getInt:GenericMessageRequest_KEY_ROOM_ID];
	id <Room> room = [_sfs.roomManager getRoomById:rId];
	
	if (room != nil) {
		
		[evtParams setValue:room forKey:@"room"];
		[evtParams setValue:[_sfs.userManager getUserById:[sfso getInt:GenericMessageRequest_KEY_USER_ID]] forKey:@"sender"];
		[evtParams setValue:[sfso getUtfString:GenericMessageRequest_KEY_MESSAGE] forKey:@"message"];
		[evtParams setValue:[sfso getSFSObject:GenericMessageRequest_KEY_XTRA_PARAMS] forKey:@"data"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onPublicMessage:)]) {
			[_sfs.delegate onPublicMessage:[[SFSEvent alloc] initWithType:SFSEvent_PUBLIC_MESSAGE params:evtParams]];
		}
		
	}
	else {
		[_log warn:[NSString stringWithFormat:@"Unexpected, PublicMessage target room doesn't exist. RoomId: %d", rId]];
	}

	
}

-(void)handlePrivateMessage:(id <ISFSObject>)sfso {
	
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	int senderId = (int)[sfso getInt:GenericMessageRequest_KEY_USER_ID];
	
	id <User> sender = [_sfs.userManager getUserById:senderId];
	
	if (sender == nil) {
		if (![sfso containsKey:GenericMessageRequest_KEY_SENDER_DATA]) {
			[_log warn:@"Unexpected. Private message has no Sender details!"];
			return;
		}
		
		sender = [SFSUser fromSFSArray:[sfso getSFSArray:GenericMessageRequest_KEY_SENDER_DATA] room:nil];
	}
	
	
	[evtParams setValue:sender forKey:@"sender"];
	[evtParams setValue:[sfso getUtfString:GenericMessageRequest_KEY_MESSAGE] forKey:@"message"];
	[evtParams setValue:[sfso getSFSObject:GenericMessageRequest_KEY_XTRA_PARAMS] forKey:@"data"];
	
	if ([_sfs.delegate respondsToSelector:@selector(onPrivateMessage:)]) {
		[_sfs.delegate onPrivateMessage:[[SFSEvent alloc] initWithType:SFSEvent_PRIVATE_MESSAGE params:evtParams]];
	}
	
}

-(void)handleBuddyMessage:(id <ISFSObject>)sfso {
	
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	int senderId = (int)[sfso getInt:GenericMessageRequest_KEY_USER_ID];
	
	id <Buddy> senderBuddy = [_sfs.buddyManager getBuddyById:senderId];
    
    BOOL isItMe = [[senderBuddy name] isEqualToString:[_sfs.mySelf name]];

	[evtParams setValue:[NSNumber numberWithBool:isItMe] forKey:@"isItMe"];
	//[_sfs.mySelf setId:senderId];
	[evtParams setValue:senderBuddy forKey:@"buddy"];
	[evtParams setValue:[sfso getUtfString:GenericMessageRequest_KEY_MESSAGE] forKey:@"message"];
	[evtParams setValue:[sfso getSFSObject:GenericMessageRequest_KEY_XTRA_PARAMS] forKey:@"data"];
	
	if ([_sfs.delegate respondsToSelector:@selector(onBuddyMessage:)])
    {
		[_sfs.delegate onBuddyMessage:[[SFSBuddyEvent alloc] initWithType:SFSBuddyEvent_BUDDY_MESSAGE params:evtParams]];
	}
	
}
-(void)handleModMessage:(id <ISFSObject>)sfso {
	
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	[evtParams setValue:[SFSUser fromSFSArray:[sfso getSFSArray:GenericMessageRequest_KEY_SENDER_DATA] room:nil] forKey:@"sender"];
	[evtParams setValue:[sfso getUtfString:GenericMessageRequest_KEY_MESSAGE] forKey:@"message"];
	[evtParams setValue:[sfso getSFSObject:GenericMessageRequest_KEY_XTRA_PARAMS] forKey:@"data"];
	
	if ([_sfs.delegate respondsToSelector:@selector(onModeratorMessage:)]) {
		[_sfs.delegate onModeratorMessage:[[SFSEvent alloc] initWithType:SFSEvent_MODERATOR_MESSAGE params:evtParams]];
	}
	
}

-(void)handleAdminMessage:(id <ISFSObject>)sfso {
	
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	[evtParams setValue:[SFSUser fromSFSArray:[sfso getSFSArray:GenericMessageRequest_KEY_SENDER_DATA] room:nil] forKey:@"sender"];
	[evtParams setValue:[sfso getUtfString:GenericMessageRequest_KEY_MESSAGE] forKey:@"message"];
	[evtParams setValue:[sfso getSFSObject:GenericMessageRequest_KEY_XTRA_PARAMS] forKey:@"data"];
	
	if ([_sfs.delegate respondsToSelector:@selector(onAdminMessage:)]) {
		[_sfs.delegate onAdminMessage:[[SFSEvent alloc] initWithType:SFSEvent_ADMIN_MESSAGE params:evtParams]];
	}
}

-(void)handleObjectMessage:(id <ISFSObject>)sfso {
	
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	int senderId = (int)[sfso getInt:GenericMessageRequest_KEY_USER_ID];
	
	[evtParams setValue:[_sfs.userManager getUserById:senderId] forKey:@"sender"];
	[evtParams setValue:[sfso getSFSObject:GenericMessageRequest_KEY_XTRA_PARAMS] forKey:@"message"];
	
	
	if ([_sfs.delegate respondsToSelector:@selector(onObjectMessage:)]) {
		[_sfs.delegate onObjectMessage:[[SFSEvent alloc] initWithType:SFSEvent_OBJECT_MESSAGE params:evtParams]];
	}
}

-(void)fnGenericMessage:(id <IMessage>)msg {
	id <ISFSObject> sfso = [msg content];
	
	int msgType = (int)[sfso getByte:GenericMessageRequest_KEY_MESSAGE_TYPE];
	
	if (msgType == GenericMessageType_PUBLIC_MSG) {
		[self handlePublicMessage:sfso];
	}
	else if (msgType == GenericMessageType_PRIVATE_MSG) {
		[self handlePrivateMessage:sfso];
	}
	else if (msgType == GenericMessageType_BUDDY_MSG) {
		[self handleBuddyMessage:sfso];
	}
	else if (msgType == GenericMessageType_MODERATOR_MSG) {
		[self handleModMessage:sfso];
	}
	else if (msgType == GenericMessageType_ADMING_MSG) {
		[self handleAdminMessage:sfso];
	}
	else if (msgType == GenericMessageType_OBJECT_MSG) {
		[self handleObjectMessage:sfso];
	}
	
	
}

-(void)fnUserExitRoom:(id <IMessage>)msg
{	
	id <ISFSObject> sfso = [msg content];
	
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	int rId = (int)[sfso getInt:@"r"];
	int uId = (int)[sfso getInt:@"u"];
	id <Room> room = [_sfs.roomManager getRoomById:rId];
	id <User> user = [_sfs.userManager getUserById:uId];
	
	if (room != nil && user != nil)
    {
		//NSLog(@"USER LEFT>>>>>>>>>>>>>>>>>>>>>>>> : %@", [user name]);
		[room removeUser:user];
		[_sfs.userManager removeUser:user];
        
       // _sfs.roomManager getUserRooms:<#(id<User>)#>
		
		if ([user isItMe] && [room isJoined])
        {
			[room setIsJoined:NO];
			
			// SFSBUG ? unnecessary
			[ArrayUtil removeElement:_sfs.joinedRooms item:room];
			
			if ([_sfs.joinedRooms count] == 0)
            {
				_sfs.lastJoinedRoom = nil;
			}
			
			if (![room isManaged])
            {
				[_sfs.roomManager removeRoom:room];
			}
		}
		
		[evtParams setValue:user forKey:@"user"];
		[evtParams setValue:room forKey:@"room"];
		
		
		if ([_sfs.delegate respondsToSelector:@selector(onUserExitRoom:)])
        {
			[_sfs.delegate onUserExitRoom:[[SFSEvent alloc] initWithType:SFSEvent_USER_EXIT_ROOM params:evtParams]];
		}
	}
	
	else {
		[_log debug:[NSString stringWithFormat:@"Failed to handle UserExit event. Room: %@, User: %@", room, user]];
	}
	
}

-(void)fnClientDisconnection:(id <IMessage>)msg {
		
	id <ISFSObject> sfso = [msg content];
	int reasonId = (int)[sfso getByte:@"dr"];
	
	[_sfs handleClientDisconnection:[ClientDisconnectionReason getReason:reasonId]];
	
}

-(void)fnReconnectionFailure:(id<IMessage>) msg
{
    [_sfs handleReconnectionFailure];
}


-(void)fnSetRoomVariables:(id <IMessage>)msg {
	
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	int rId = (int)[sfso getInt:SetRoomVariablesRequest_KEY_VAR_ROOM];
	id <ISFSArray> varListData = [sfso getSFSArray:SetRoomVariablesRequest_KEY_VAR_LIST];
	
	id <Room> targetRoom = [_sfs.roomManager getRoomById:rId];
	
	NSMutableArray *changedVarNames = [NSMutableArray array];
	
	if (targetRoom != nil) {
			
		for (int j = 0; j < [varListData size]; j++) {
			
			id <RoomVariable> roomVar = [SFSRoomVariable fromSFSArray:[varListData getSFSArray:j]];
			[targetRoom setVariable:roomVar];
			
			[changedVarNames addObject:[(id<UserVariable>)roomVar name]];
			
		}
		
		[evtParams setValue:changedVarNames forKey:@"changedVars"];
		[evtParams setValue:targetRoom forKey:@"room"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onRoomVariablesUpdate:)]) {
			[_sfs.delegate onRoomVariablesUpdate:[[SFSEvent alloc] initWithType:SFSEvent_ROOM_VARIABLES_UPDATE params:evtParams]];
		}
		
	}
	
	else {
		[_log warn:[NSString stringWithFormat:@"RoomVariablesUpdate, unknown Room id = %d", rId]];
	}
	
}


-(void)fnSetUserVariables:(id <IMessage>)msg {
	
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	int uId = (int)[sfso getInt:SetUserVariablesRequest_KEY_USER];
	id <ISFSArray> varListData = [sfso getSFSArray:SetUserVariablesRequest_KEY_VAR_LIST];
	
	id <User> user = [_sfs.userManager getUserById:uId];
	NSMutableArray *changedVarNames = [NSMutableArray array];
	
	
	if (user != nil) {
		
		for (int j = 0; j < [varListData size]; j++) {
			
			id <UserVariable> userVar = [SFSUserVariable fromSFSArray:[varListData getSFSArray:j]];
			[user setVariable:userVar];
			
			[changedVarNames addObject:[userVar name]];
			
		}
		
		[evtParams setValue:changedVarNames forKey:@"changedVars"];
		[evtParams setValue:user forKey:@"user"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onUserVariablesUpdate:)]) {
			[_sfs.delegate onUserVariablesUpdate:[[SFSEvent alloc] initWithType:SFSEvent_USER_VARIABLES_UPDATE params:evtParams]];
		}
		
	}
	
	else {
		[_log warn:[NSString stringWithFormat:@"UserVariablesUpdate: unknown user id = %d", uId]];
	}

	
}

-(void)fnSubscribeRoomGroup:(id <IMessage>)msg {
	
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	if ([sfso isNull:BaseRequest_KEY_ERROR_CODE]) {
		
		NSString *groupId = [sfso getUtfString:SubscribeRoomGroupRequest_KEY_GROUP_ID];
		id <ISFSArray> roomListData = [sfso getSFSArray:SubscribeRoomGroupRequest_KEY_ROOM_LIST];
		
		if ([_sfs.roomManager containsGroup:groupId]) {
			[_log warn:[NSString stringWithFormat:@"SubscribeGroup Error. Group:%@ already subscribed!", groupId]];
		}
		
        // Add Group
        [_sfs.roomManager addGroup:groupId];
        
        // Populate Room List
		[self populateRoomList:roomListData];
		
		[evtParams setValue:groupId forKey:@"groupId"];
		
		[evtParams setValue:[_sfs.roomManager getRoomListFromGroup:groupId] forKey:@"newRooms"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onSubscribeRoomGroup:)]) {
			[_sfs.delegate onSubscribeRoomGroup:[[SFSEvent alloc] initWithType:SFSEvent_ROOM_GROUP_SUBSCRIBE params:evtParams]];
		}
		
	}
	
	else {
		NSString *errorMsg = [SFSErrorCodes getErrorMessage:[NSNumber numberWithShort:[sfso getShort:BaseRequest_KEY_ERROR_CODE]] params:[sfso getUtfStringArray:BaseRequest_KEY_ERROR_PARAMS]];
		[evtParams setValue:errorMsg forKey:@"errorMessage"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onSubscribeRoomGroupError:)]) {
			[_sfs.delegate onSubscribeRoomGroupError:[[SFSEvent alloc] initWithType:SFSEvent_ROOM_GROUP_SUBSCRIBE_ERROR params:evtParams]];
		}
	}

	
}


-(void)fnUnsubscribeRoomGroup:(id <IMessage>)msg {
	
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	if ([sfso isNull:BaseRequest_KEY_ERROR_CODE]) {
		
		NSString *groupId = [sfso getUtfString:SubscribeRoomGroupRequest_KEY_GROUP_ID];
		
		if (![_sfs.roomManager containsGroup:groupId]) {
			[_log warn:[NSString stringWithFormat:@"UnsubscribeGroup Error. Group:%@ is not subscribed!", groupId]];
		}
		
		[_sfs.roomManager removeGroup:groupId];
		
		[evtParams setValue:groupId forKey:@"groupId"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onUnsubscribeRoomGroup:)]) {
			[_sfs.delegate onUnsubscribeRoomGroup:[[SFSEvent alloc] initWithType:SFSEvent_ROOM_GROUP_UNSUBSCRIBE params:evtParams]];
		}
		
	}
	
	else {
		NSString *errorMsg = [SFSErrorCodes getErrorMessage:[NSNumber numberWithShort:[sfso getShort:BaseRequest_KEY_ERROR_CODE]] params:[sfso getUtfStringArray:BaseRequest_KEY_ERROR_PARAMS]];
		[evtParams setValue:errorMsg forKey:@"errorMessage"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onUnsubscribeRoomGroupError:)]) {
			[_sfs.delegate onUnsubscribeRoomGroupError:[[SFSEvent alloc] initWithType:SFSEvent_ROOM_GROUP_UNSUBSCRIBE_ERROR params:evtParams]];
		}
	}
	
}


-(void)fnChangeRoomName:(id <IMessage>)msg {
	
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	if ([sfso isNull:BaseRequest_KEY_ERROR_CODE]) {
	
		int roomId = (int)[sfso getInt:ChangeRoomNameRequest_KEY_ROOM];
		id <Room> targetRoom = [_sfs.roomManager getRoomById:roomId];
		
		if (targetRoom != nil) {
			[evtParams setValue:[targetRoom name] forKey:@"oldName"];
			[_sfs.roomManager changeRoomName:targetRoom newName:[sfso getUtfString:ChangeRoomNameRequest_KEY_NAME]];
			
			[evtParams setValue:targetRoom forKey:@"room"];
			
			if ([_sfs.delegate respondsToSelector:@selector(onRoomNameChange:)]) {
				[_sfs.delegate onRoomNameChange:[[SFSEvent alloc] initWithType:SFSEvent_ROOM_NAME_CHANGE params:evtParams]];
			}
			
		}
	}
	
	else {
		NSString *errorMsg = [SFSErrorCodes getErrorMessage:[NSNumber numberWithShort:[sfso getShort:BaseRequest_KEY_ERROR_CODE]] params:[sfso getUtfStringArray:BaseRequest_KEY_ERROR_PARAMS]];
		[evtParams setValue:errorMsg forKey:@"errorMessage"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onRoomNameChangeError:)]) {
			[_sfs.delegate onRoomNameChangeError:[[SFSEvent alloc] initWithType:SFSEvent_ROOM_NAME_CHANGE_ERROR params:evtParams]];
		}
	}

	
}

-(void)fnChangeRoomPassword:(id <IMessage>)msg {
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	if ([sfso isNull:BaseRequest_KEY_ERROR_CODE]) {
		
		int roomId = (int)[sfso getInt:ChangeRoomPasswordStateRequest_KEY_ROOM];
		id <Room> targetRoom = [_sfs.roomManager getRoomById:roomId];
		
		if (targetRoom != nil) {
			
			[_sfs.roomManager changeRoomPasswordState:targetRoom isPassProtected:[sfso getBool:ChangeRoomPasswordStateRequest_KEY_PASS]];
			
			[evtParams setValue:targetRoom forKey:@"room"];
			
			if ([_sfs.delegate respondsToSelector:@selector(onRoomPasswordStateChange:)]) {
				[_sfs.delegate onRoomPasswordStateChange:[[SFSEvent alloc] initWithType:SFSEvent_ROOM_PASSWORD_STATE_CHANGE params:evtParams]];
			}
			
			
		}
		else {
			[_log warn:[NSString stringWithFormat:@"Room not found, ID:%d, Room password change failed.", roomId]];
		}

	
	}
	else {
		NSString *errorMsg = [SFSErrorCodes getErrorMessage:[NSNumber numberWithShort:[sfso getShort:BaseRequest_KEY_ERROR_CODE]] params:[sfso getUtfStringArray:BaseRequest_KEY_ERROR_PARAMS]];
		[evtParams setValue:errorMsg forKey:@"errorMessage"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onRoomPasswordStateChangeError:)]) {
			[_sfs.delegate onRoomPasswordStateChangeError:[[SFSEvent alloc] initWithType:SFSEvent_ROOM_PASSWORD_STATE_CHANGE_ERROR params:evtParams]];
		}
	}
}

-(void)fnChangeRoomCapacity:(id <IMessage>)msg {
	
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	
	if ([sfso isNull:BaseRequest_KEY_ERROR_CODE]) {
		
		int roomId = (int)[sfso getInt:ChangeRoomCapacityRequest_KEY_ROOM];
		id <Room> targetRoom = [_sfs.roomManager getRoomById:roomId];
		
		if (targetRoom != nil) {
			
			[_sfs.roomManager changeRoomCapacity:targetRoom maxUsers:[sfso getInt:ChangeRoomCapacityRequest_KEY_USER_SIZE] maxSpect:[sfso getInt:ChangeRoomCapacityRequest_KEY_SPEC_SIZE]];
			
			[evtParams setValue:targetRoom forKey:@"room"];
			
			if ([_sfs.delegate respondsToSelector:@selector(onRoomCapacityChange:)]) {
				[_sfs.delegate onRoomCapacityChange:[[SFSEvent alloc] initWithType:SFSEvent_ROOM_CAPACITY_CHANGE params:evtParams]];
			}
			
		}
		
		else {
			[_log warn:[NSString stringWithFormat:@"Room not found, ID:%d, Room capacity change failed.", roomId]];
		}

		
	}
	
	else {
		NSString *errorMsg = [SFSErrorCodes getErrorMessage:[NSNumber numberWithShort:[sfso getShort:BaseRequest_KEY_ERROR_CODE]] params:[sfso getUtfStringArray:BaseRequest_KEY_ERROR_PARAMS]];
		[evtParams setValue:errorMsg forKey:@"errorMessage"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onRoomCapacityChangeError:)]) {
			[_sfs.delegate onRoomCapacityChangeError:[[SFSEvent alloc] initWithType:SFSEvent_ROOM_CAPACITY_CHANGE_ERROR params:evtParams]];
		}
	}

	
	
}


-(void)fnLogout:(id <IMessage>)msg {
	
	
	[_sfs handleLogout];
	
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	[evtParams setValue:[sfso getUtfString:LogoutRequest_KEY_ZONE_NAME] forKey:@"zoneName"];
	
	
	
	if ([_sfs.delegate respondsToSelector:@selector(onLogout:)]) {
		[_sfs.delegate onLogout:[[SFSEvent alloc] initWithType:SFSEvent_LOGOUT params:evtParams]];
	}
	
}

-(void)fnSpectatorToPlayer:(id <IMessage>)msg {
	
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	if ([sfso isNull:BaseRequest_KEY_ERROR_CODE]) {
		int roomId = (int)[sfso getInt:SpectatorToPlayerRequest_KEY_ROOM_ID];
		int userId = (int)[sfso getInt:SpectatorToPlayerRequest_KEY_USER_ID];
		int playerId = (int)[sfso getInt:SpectatorToPlayerRequest_KEY_PLAYER_ID];
		
		id <User> user = [_sfs.userManager getUserById:userId];
		id <Room> targetRoom = [_sfs.roomManager getRoomById:roomId];
		
		if (targetRoom != nil) {
			if (user != nil) {
				if ([user isJoinedInRoom:targetRoom]) {
					[user setPlayerId:playerId room:targetRoom];
					
					[evtParams setValue:targetRoom forKey:@"room"];
					[evtParams setValue:user forKey:@"user"];
					[evtParams setValue:[NSNumber numberWithInt:playerId] forKey:@"playerId"];
					
					if ([_sfs.delegate respondsToSelector:@selector(onSpectatorToPlayer:)]) {
						[_sfs.delegate onSpectatorToPlayer:[[SFSEvent alloc] initWithType:SFSEvent_SPECTATOR_TO_PLAYER params:evtParams]];
					}
				}
				else {
					[_log warn:[NSString stringWithFormat:@"User: %@ not joined in Room: %@, SpectatorToPlayer failed.", user, targetRoom]];
				}
			}
			else {
				[_log warn:[NSString stringWithFormat:@"User not found, ID:%d SpectatorToPlayer failed.", userId]];
			}
		}
		else {
			[_log warn:[NSString stringWithFormat:@"Room not found, ID:%d SpectatorToPlayer failed.", roomId]];
		}
	}
	else {
		NSString *errorMsg = [SFSErrorCodes getErrorMessage:[NSNumber numberWithShort:[sfso getShort:BaseRequest_KEY_ERROR_CODE]] params:[sfso getUtfStringArray:BaseRequest_KEY_ERROR_PARAMS]];
		[evtParams setValue:errorMsg forKey:@"errorMessage"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onSpectatorToPlayerError:)]) {
			[_sfs.delegate onSpectatorToPlayerError:[[SFSEvent alloc] initWithType:SFSEvent_SPECTATOR_TO_PLAYER_ERROR params:evtParams]];
		}
	}

}

-(void)fnPlayerToSpectator:(id <IMessage>)msg {
	
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	if ([sfso isNull:BaseRequest_KEY_ERROR_CODE]) {
		int roomId = (int)[sfso getInt:PlayerToSpectatorRequest_KEY_ROOM_ID];
		int userId = (int)[sfso getInt:PlayerToSpectatorRequest_KEY_USER_ID];
		
		id <User> user = [_sfs.userManager getUserById:userId];
		id <Room> targetRoom = [_sfs.roomManager getRoomById:roomId];
		
		if (targetRoom != nil) {
			if (user != nil) {
				if ([user isJoinedInRoom:targetRoom]) {
					[user setPlayerId:-1 room:targetRoom];
					
					[evtParams setValue:targetRoom forKey:@"room"];
					[evtParams setValue:user forKey:@"user"];
					
					if ([_sfs.delegate respondsToSelector:@selector(onPlayerToSpectator:)]) {
						[_sfs.delegate onPlayerToSpectator:[[SFSEvent alloc] initWithType:SFSEvent_PLAYER_TO_SPECTATOR params:evtParams]];
					}
				}
				else {
					[_log warn:[NSString stringWithFormat:@"User: %@ not joined in Room: %@, PlayerToSpectator failed.", user, targetRoom]];
				}
			}
			else {
				[_log warn:[NSString stringWithFormat:@"User not found, ID:%d PlayerToSpectator failed.", userId]];
			}
		}
		else {
			[_log warn:[NSString stringWithFormat:@"Room not found, ID:%d PlayerToSpectator failed.", roomId]];
		}
	}
	else {
		NSString *errorMsg = [SFSErrorCodes getErrorMessage:[NSNumber numberWithShort:[sfso getShort:BaseRequest_KEY_ERROR_CODE]] params:[sfso getUtfStringArray:BaseRequest_KEY_ERROR_PARAMS]];
		[evtParams setValue:errorMsg forKey:@"errorMessage"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onPlayerToSpectatorError:)]) {
			[_sfs.delegate onPlayerToSpectatorError:[[SFSEvent alloc] initWithType:SFSEvent_PLAYER_TO_SPECTATOR_ERROR params:evtParams]];
		}
	}
	
}

-(void)fnInitBuddyList:(id <IMessage>)msg {

	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	if ([sfso isNull:BaseRequest_KEY_ERROR_CODE]) {
		id <ISFSArray> bListData = [sfso getSFSArray:InitBuddyListRequest_KEY_BLIST];
		id <ISFSArray> myVarsData = [sfso getSFSArray:InitBuddyListRequest_KEY_MY_VARS];
		NSArray *buddyStates = [sfso getUtfStringArray:InitBuddyListRequest_KEY_BUDDY_STATES];
		
		[_sfs.buddyManager clearAll];
		
		for (int i = 0; i < [bListData size]; i++) {
			id <Buddy> b = [SFSBuddy fromSFSArray:[bListData getSFSArray:i]];
			[(SFSBuddyManager *)_sfs.buddyManager addBuddy:b];
		}
		
		if (buddyStates != nil) {
			[_sfs.buddyManager setBuddyStates:buddyStates];
		}
		
		NSMutableArray *myBuddyVariables = [NSMutableArray array];
		
		for (int i = 0; i < [myVarsData size]; i++) {
			[myBuddyVariables addObject:[SFSBuddyVariable fromSFSArray:[myVarsData getSFSArray:i]]];
		}
		
		[_sfs.buddyManager setMyVariables:myBuddyVariables];
		[(SFSBuddyManager *)_sfs.buddyManager setInited:YES];
		
		[evtParams setValue:[_sfs.buddyManager buddyList] forKey:@"buddyList"];
		[evtParams setValue:[_sfs.buddyManager myVariables] forKey:@"myVariables"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onBuddyListInit:)]) {
			[_sfs.delegate onBuddyListInit:[[SFSBuddyEvent alloc] initWithType:SFSBuddyEvent_BUDDY_LIST_INIT params:evtParams]];
		}
	}
	else {
		NSString *errorMsg = [SFSErrorCodes getErrorMessage:[NSNumber numberWithShort:[sfso getShort:BaseRequest_KEY_ERROR_CODE]] params:[sfso getUtfStringArray:BaseRequest_KEY_ERROR_PARAMS]];
		[evtParams setValue:errorMsg forKey:@"errorMessage"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onBuddyError:)]) {
			[_sfs.delegate onBuddyError:[[SFSBuddyEvent alloc] initWithType:SFSBuddyEvent_BUDDY_ERROR params:evtParams]];
		}
	}
	
}

-(void)fnAddBuddy:(id <IMessage>)msg {
	
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	if ([sfso isNull:BaseRequest_KEY_ERROR_CODE]) {
		id <Buddy> buddy = [SFSBuddy fromSFSArray:[sfso getSFSArray:AddBuddyRequest_KEY_BUDDY_NAME]];
		[(SFSBuddyManager *)_sfs.buddyManager addBuddy:buddy];
		
		[evtParams setValue:buddy forKey:@"buddy"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onBuddyAdd:)]) {
			[_sfs.delegate onBuddyAdd:[[SFSBuddyEvent alloc] initWithType:SFSBuddyEvent_BUDDY_ADD params:evtParams]];
		}
	}
	else {
		NSString *errorMsg = [SFSErrorCodes getErrorMessage:[NSNumber numberWithShort:[sfso getShort:BaseRequest_KEY_ERROR_CODE]] params:[sfso getUtfStringArray:BaseRequest_KEY_ERROR_PARAMS]];
		[evtParams setValue:errorMsg forKey:@"errorMessage"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onBuddyError:)]) {
			[_sfs.delegate onBuddyError:[[SFSBuddyEvent alloc] initWithType:SFSBuddyEvent_BUDDY_ERROR params:evtParams]];
		}
	}
}

-(void)fnRemoveBuddy:(id <IMessage>)msg {
	
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	if ([sfso isNull:BaseRequest_KEY_ERROR_CODE]) {
		NSString *buddyName = [sfso getUtfString:RemoveBuddyRequest_KEY_BUDDY_NAME];
		id <Buddy> buddy = [(SFSBuddyManager *)_sfs.buddyManager removeBuddyByName:buddyName];
		
		if (buddy != nil) {
			[evtParams setValue:buddy forKey:@"buddy"];
			
			if ([_sfs.delegate respondsToSelector:@selector(onBuddyRemove:)]) {
				[_sfs.delegate onBuddyRemove:[[SFSBuddyEvent alloc] initWithType:SFSBuddyEvent_BUDDY_REMOVE params:evtParams]];
			}
		}
		else {
			[_log warn:[NSString stringWithFormat:@"RemoveBuddy failed, buddy not found: %@", buddyName]];
		}

	}
	
	else {
		NSString *errorMsg = [SFSErrorCodes getErrorMessage:[NSNumber numberWithShort:[sfso getShort:BaseRequest_KEY_ERROR_CODE]] params:[sfso getUtfStringArray:BaseRequest_KEY_ERROR_PARAMS]];
		[evtParams setValue:errorMsg forKey:@"errorMessage"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onBuddyError:)]) {
			[_sfs.delegate onBuddyError:[[SFSBuddyEvent alloc] initWithType:SFSBuddyEvent_BUDDY_ERROR params:evtParams]];
		}
	}
}

-(void)fnBlockBuddy:(id <IMessage>)msg {
	
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	if ([sfso isNull:BaseRequest_KEY_ERROR_CODE])
    {
		NSString *buddyName = [sfso getUtfString:BlockBuddyRequest_KEY_BUDDY_NAME];
		id <Buddy> buddy = [(SFSBuddyManager *)_sfs.buddyManager getBuddyByName:buddyName];
		
        /*
         * New since 1.7.0
         * If buddy was un-blocked we receive the whole buddy object with his latest data
         */
        if ([sfso containsKey:BlockBuddyRequest_KEY_BUDDY])
        {
            buddy = [SFSBuddy fromSFSArray:[sfso getSFSArray:BlockBuddyRequest_KEY_BUDDY]];
            [(SFSBuddyManager*) _sfs.buddyManager addBuddy:buddy];
        }
        
        /*
         * Executes only if buddy was blocked
         */
        else if (buddy != nil)
        {
            // Set the buddy block state
			[buddy setBlocked:[sfso getBool:BlockBuddyRequest_KEY_BUDDY_BLOCK_STATE]];
		}
		
        else
        {
			[_log warn:[NSString stringWithFormat:@"BlockBuddy failed, buddy not found: %@, in local BuddyList", buddyName]];
            return;
		}
        
        // Fire event
        [evtParams setValue:buddy forKey:@"buddy"];
        
        if ([_sfs.delegate respondsToSelector:@selector(onBuddyBlock:)]) {
            [_sfs.delegate onBuddyBlock:[[SFSBuddyEvent alloc] initWithType:SFSBuddyEvent_BUDDY_BLOCK params:evtParams]];
        }

	}
	
	else {
		NSString *errorMsg = [SFSErrorCodes getErrorMessage:[NSNumber numberWithShort:[sfso getShort:BaseRequest_KEY_ERROR_CODE]] params:[sfso getUtfStringArray:BaseRequest_KEY_ERROR_PARAMS]];
		[evtParams setValue:errorMsg forKey:@"errorMessage"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onBuddyError:)]) {
			[_sfs.delegate onBuddyError:[[SFSBuddyEvent alloc] initWithType:SFSBuddyEvent_BUDDY_ERROR params:evtParams]];
		}
	}
}

-(void)fnGoOnline:(id <IMessage>)msg {
	
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	if ([sfso isNull:BaseRequest_KEY_ERROR_CODE]) {
		NSString *buddyName = [sfso getUtfString:GoOnlineRequest_KEY_BUDDY_NAME];
		id <Buddy> buddy = [(SFSBuddyManager *)_sfs.buddyManager getBuddyByName:buddyName];
		BOOL isItMe = [buddyName isEqualToString:[_sfs.mySelf name]];
		int onlineValue = (int)[sfso getByte:GoOnlineRequest_KEY_ONLINE];
		BOOL onlineState = (onlineValue == BuddyOnlineState_ONLINE);
		
		BOOL fireEvent = YES;
		
		if (isItMe) {
			if ([_sfs.buddyManager myOnlineState] != onlineState) {
				[_log warn:[NSString stringWithFormat:@"Unexpected: MyOnlineState is not in synch with the server. Resynching: %@", [NSNumber numberWithBool:onlineState]]];
				[_sfs.buddyManager setMyOnlineState:onlineState];
			}
		}
		else if (buddy != nil){
			[buddy setId:[sfso getInt:GoOnlineRequest_KEY_BUDDY_ID]];
			[buddy setVariable:[[SFSBuddyVariable alloc] initWithName:ReservedBuddyVariables_BV_ONLINE value:[NSNumber numberWithBool:onlineState] type:-1]];
			
			if (onlineValue == BuddyOnlineState_LEFT_THE_SERVER) {
				[buddy clearVolatileVariables];
			}
			
			fireEvent = [_sfs.buddyManager myOnlineState];
		}
		else {
			[_log warn:[NSString stringWithFormat:@"GoOnline error, buddy not found: %@, in local BuddyList.", buddyName]];
			return;
		}
		
		if (fireEvent) {
			[evtParams setValue:buddy forKey:@"buddy"];
			[evtParams setValue:[NSNumber numberWithBool:isItMe] forKey:@"isItMe"];
			
			if ([_sfs.delegate respondsToSelector:@selector(onBuddyOnlineStateUpdate:)]) {
				[_sfs.delegate onBuddyOnlineStateUpdate:[[SFSBuddyEvent alloc] initWithType:SFSBuddyEvent_BUDDY_ONLINE_STATE_UPDATE params:evtParams]];
			}
		}

		
	}
	
	else {
		NSString *errorMsg = [SFSErrorCodes getErrorMessage:[NSNumber numberWithShort:[sfso getShort:BaseRequest_KEY_ERROR_CODE]] params:[sfso getUtfStringArray:BaseRequest_KEY_ERROR_PARAMS]];
		[evtParams setValue:errorMsg forKey:@"errorMessage"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onBuddyError:)]) {
			[_sfs.delegate onBuddyError:[[SFSBuddyEvent alloc] initWithType:SFSBuddyEvent_BUDDY_ERROR params:evtParams]];
		}
	}
}

-(void)fnSetBuddyVariables:(id <IMessage>)msg {
	
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	if ([sfso isNull:BaseRequest_KEY_ERROR_CODE]) {
		NSString *buddyName = [sfso getUtfString:SetBuddyVariablesRequest_KEY_BUDDY_NAME];
		id <ISFSArray> buddyVarsData = [sfso getSFSArray:SetBuddyVariablesRequest_KEY_BUDDY_VARS];
		
		id <Buddy> buddy = [_sfs.buddyManager getBuddyByName:buddyName];
		
		BOOL isItMe = [buddyName isEqualToString:[_sfs.mySelf name]];
		
		NSMutableArray *changedVarNames = [NSMutableArray array];
		NSMutableArray *variables = [NSMutableArray array];
		
		BOOL fireEvent = YES;
		
		for (int j = 0; j < [buddyVarsData size]; j++) {
			id <BuddyVariable> buddyVar = [SFSBuddyVariable fromSFSArray:[buddyVarsData getSFSArray:j]];
			
			[variables addObject:buddyVar];
			[changedVarNames addObject:[buddyVar name]];
		}
		
		if (isItMe) {
			[_sfs.buddyManager setMyVariables:variables];
		}
		else if (buddy != nil) {
			
			[buddy setVariables:variables];
			
			fireEvent = [_sfs.buddyManager myOnlineState];
		}
		else {
			[_log warn:[NSString stringWithFormat:@"Unexpected. Target of BuddyVariables update not found: %@", buddyName]];
			return;
		}
		
		if (fireEvent) {
			[evtParams setValue:[NSNumber numberWithBool:isItMe] forKey:@"isItMe"];
			[evtParams setValue:changedVarNames forKey:@"changedVars"];
			[evtParams setValue:buddy forKey:@"buddy"];
			
			if ([_sfs.delegate respondsToSelector:@selector(onBuddyVariablesUpdate:)]) {
				[_sfs.delegate onBuddyVariablesUpdate:[[SFSBuddyEvent alloc] initWithType:SFSBuddyEvent_BUDDY_VARIABLES_UPDATE params:evtParams]];
			}
		}

	}
	else {
		NSString *errorMsg = [SFSErrorCodes getErrorMessage:[NSNumber numberWithShort:[sfso getShort:BaseRequest_KEY_ERROR_CODE]] params:[sfso getUtfStringArray:BaseRequest_KEY_ERROR_PARAMS]];
		[evtParams setValue:errorMsg forKey:@"errorMessage"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onBuddyError:)]) {
			[_sfs.delegate onBuddyError:[[SFSBuddyEvent alloc] initWithType:SFSBuddyEvent_BUDDY_ERROR params:evtParams]];
		}
	}
}

-(void)fnFindRooms:(id <IMessage>)msg {
	
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	id <ISFSArray> roomListData = [sfso getSFSArray:FindRoomsRequest_KEY_FILTERED_ROOMS];
	NSMutableArray *roomList = [NSMutableArray array];
	
	for (int i = 0; i < [roomListData size]; i++)
    {
        SFSRoom *theRoom = [SFSRoom fromSFSArray:[roomListData getSFSArray:i]];
        
        // Flag Rooms that are locally joined
        SFSRoom *localRoom = [_sfs.roomManager getRoomById:theRoom.id];
        
        if (localRoom != nil)
            theRoom.isJoined = localRoom.isJoined;
            
		[roomList addObject: theRoom];
	}
	
	[evtParams setValue:roomList forKey:@"rooms"];
	
	if ([_sfs.delegate respondsToSelector:@selector(onRoomFindResult:)])
    {
		[_sfs.delegate onRoomFindResult:[[SFSEvent alloc] initWithType:SFSEvent_ROOM_FIND_RESULT params:evtParams]];
	}
}

-(void)fnFindUsers:(id <IMessage>)msg {
	
	id <ISFSObject> sfso = [msg content];
    
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	id <ISFSArray> userListData = [sfso getSFSArray:FindUsersRequest_KEY_FILTERED_USERS];
	NSMutableArray *userList = [NSMutableArray array];
	
	for (int i = 0; i < [userListData size]; i++)
    {
        // Added user id check so we can switch if it's my user object
        
        SFSUser *usr = [SFSUser fromSFSArray:[userListData getSFSArray:i] room:nil];
        
        if([usr id] == [_sfs.mySelf id])
        {
            usr = _sfs.mySelf;
        }
           
        [userList addObject:usr];
        
	}
	
	[evtParams setValue:userList forKey:@"users"];
	
	if ([_sfs.delegate respondsToSelector:@selector(onUserFindResult:)])
    {
		[_sfs.delegate onUserFindResult:[[SFSEvent alloc] initWithType:SFSEvent_USER_FIND_RESULT params:evtParams]];
	}
}

-(void)fnInviteUsers:(id <IMessage>)msg {
	
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	id <User> inviter = nil;
	
	if ([sfso containsKey:InviteUsersRequest_KEY_USER_ID]) {
		inviter = [_sfs.userManager getUserById:[sfso getInt:InviteUsersRequest_KEY_USER_ID]];
	}
	else {
		inviter = [SFSUser fromSFSArray:[sfso getSFSArray:InviteUsersRequest_KEY_USER] room:nil];
	}
	
	int expiryTime = (int)[sfso getShort:InviteUsersRequest_KEY_TIME];
	int invitationId = (int)[sfso getInt:InviteUsersRequest_KEY_INVITATION_ID];
	id <ISFSObject> invParams = [sfso getSFSObject:InviteUsersRequest_KEY_PARAMS];
	
	id <Invitation> invitation = [[SFSInvitation alloc] initWithInviter:inviter invitee:_sfs.mySelf secondsForAnswer:expiryTime params:invParams];
	[invitation setId:invitationId];
	
	[evtParams setValue:invitation forKey:@"invitation"];
	
	if ([_sfs.delegate respondsToSelector:@selector(onInvitation:)]) {
		[_sfs.delegate onInvitation:[[SFSEvent alloc] initWithType:SFSEvent_INVITATION params:evtParams]];
	}
	
}

-(void)fnInvitationReply:(id <IMessage>)msg {

	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	if ([sfso isNull:BaseRequest_KEY_ERROR_CODE]) {
		id <User> invitee = nil;
		
		if ([sfso containsKey:InviteUsersRequest_KEY_USER_ID]) {
			invitee = [_sfs.userManager getUserById:[sfso getInt:InviteUsersRequest_KEY_USER_ID]];
		}
		else {
			invitee = [SFSUser fromSFSArray:[sfso getSFSArray:InviteUsersRequest_KEY_USER] room:nil];
		}

		int reply = (int)[sfso getByte:InviteUsersRequest_KEY_REPLY_ID];
		id <ISFSObject> data = [sfso getSFSObject:InviteUsersRequest_KEY_PARAMS];
		
		[evtParams setValue:invitee forKey:@"invitee"];
		[evtParams setValue:[NSNumber numberWithInt:reply] forKey:@"reply"];
		[evtParams setValue:data forKey:@"data"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onInvitationReply:)]) {
			[_sfs.delegate onInvitationReply:[[SFSEvent alloc] initWithType:SFSEvent_INVITATION_REPLY params:evtParams]];
		}
	}
	
	else {
		NSString *errorMsg = [SFSErrorCodes getErrorMessage:[NSNumber numberWithShort:[sfso getShort:BaseRequest_KEY_ERROR_CODE]] params:[sfso getUtfStringArray:BaseRequest_KEY_ERROR_PARAMS]];
		[evtParams setValue:errorMsg forKey:@"errorMessage"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onInvitationReplyError:)]) {
			[_sfs.delegate onInvitationReplyError:[[SFSEvent alloc] initWithType:SFSEvent_INVITATION_REPLY_ERROR params:evtParams]];
		}
	}
	
}

-(void)fnQuickJoinGame:(id <IMessage>)msg
{
	
	id <ISFSObject> sfso = [msg content];
	NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
	
	if ([sfso containsKey:BaseRequest_KEY_ERROR_CODE])
    {
		NSString *errorMsg = [SFSErrorCodes getErrorMessage:[NSNumber numberWithShort:[sfso getShort:BaseRequest_KEY_ERROR_CODE]] params:[sfso getUtfStringArray:BaseRequest_KEY_ERROR_PARAMS]];
		[evtParams setValue:errorMsg forKey:@"errorMessage"];
		
		if ([_sfs.delegate respondsToSelector:@selector(onRoomJoinError:)])
        {
			[_sfs.delegate onRoomJoinError:[[SFSEvent alloc] initWithType:SFSEvent_ROOM_JOIN_ERROR params:evtParams]];
		}
	}
}

-(void)fnPingPong:(id<IMessage>)msg
{
    NSInteger avg = [[_sfs lagMonitor] onPingPong];
    
    NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
    [evtParams setValue:[NSNumber numberWithInt: (int)avg] forKey:@"lagValue"];
    
    [_sfs.delegate onPingPong:[[SFSEvent alloc] initWithType:SFSEvent_PING_PONG params:evtParams]];    
}

-(void) fnSetUserPosition:(id<IMessage>)msg
{
    id <ISFSObject> sfso = [msg content];
    NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
    
    NSInteger roomId = [sfso getInt:SetUserPositionRequest_KEY_ROOM];
    
    NSArray* minusUserList = [sfso getIntArray:SetUserPositionRequest_KEY_MINUS_USER_LIST];
    id<ISFSArray> plusUserList = [sfso getSFSArray:SetUserPositionRequest_KEY_PLUS_USER_LIST];
    
    //NSLog(@"PLUS/MINUS: %@, %@", plusUserList, minusUserList);
    
    NSArray* minusItemList = [sfso getIntArray:SetUserPositionRequest_KEY_MINUS_ITEM_LIST];
    id<ISFSArray> plusItemList = [sfso getSFSArray:SetUserPositionRequest_KEY_PLUS_ITEM_LIST];
    
    id<Room> theRoom = [[_sfs roomManager] getRoomById:roomId];
    
    NSMutableArray* addedUsers = [[NSMutableArray alloc] init];
    NSMutableArray* removedUsers = [[NSMutableArray alloc] init];
    NSMutableArray* addedItems = [[NSMutableArray alloc] init];
    NSMutableArray* removedItems = [[NSMutableArray alloc] init];

    // Remove users that no longer are in proximity
    if (minusUserList && minusUserList.count > 0)
    {
        for (NSNumber* userId in minusUserList)
        {
            id<User> removedUser = [theRoom getUserById:[userId integerValue]];
            
            if (removedUser)
            {
                [theRoom removeUser:removedUser];
                [removedUsers addObject:removedUser];
            }
        }
    }
    
    // Add new users that are now in proximity
    if (plusUserList)
    {
        for (int i = 0; i < [plusUserList size]; i++)
        {
            id<ISFSArray> encodedUser = [plusUserList getSFSArray:i];
            
            id<User> newUser = [self getOrCreateUser:encodedUser addToGlobalManager:YES room:theRoom];
            [addedUsers addObject:newUser];
            [theRoom addUser:newUser];
            
            /*
             * From the encoded User (as SFSArray) we extract the 5th extra/optional parameter
             * which contains his location on the map, or the AOIEntryPoint as we will refer to it
             */
            NSArray* userEntryPos = [encodedUser getElementAt:5];
            
            if (userEntryPos)
            {
                ((SFSUser*) newUser).aoiEntryPoint = [Vec3D fromArray:userEntryPos];
            }
        }
    }
    
    MMORoom* mmoRoom = (MMORoom*) theRoom;
    
    // If there are items to remove simply pass the list of MMOItem ids
    if (minusItemList)
    {
        for (NSNumber* itemId in minusItemList)
        {
            id<IMMOItem> mmoItem = [mmoRoom getMMOItem:[itemId integerValue]];
            
            if (mmoItem)
            {
                // Remove from Room Item list
                [mmoRoom removeMMOItem:[itemId integerValue]];
                
                // Add to event list
                [removedItems addObject:mmoItem];
            }
        }
    }
    
    // Prepare a list of new MMOItems in view
    if (plusItemList)
    {
        for (int i = 0; i < [plusItemList size]; i++)
        {
            id<ISFSArray> encodedItem = [plusItemList getSFSArray:i];
            
            // Create the MMO Item with the server side ID (Index = 0 of the SFSArray)
            id<IMMOItem> newItem = [MMOItem fromSFSArray:encodedItem];
            
            // Add to event list
            [addedItems addObject:newItem];
            
            // Add to Room Item List
            [mmoRoom addMMOItem:newItem];
            
            /*
             * From the encoded Item (as SFSArray) we extract the 2nd extra/optional parameter
             * which contains its location on the map, or the AOIEntryPoint as we will refer to it
             */
            NSArray* itemEntryPos = [encodedItem getElementAt:2];
            
            if (itemEntryPos)
            {
                ((MMOItem*) newItem).aoiEntryPoint = [Vec3D fromArray:[encodedItem getElementAt:2]];
            }
        }
    }
    
    // Prepare event parameters
    [evtParams setObject:addedItems forKey:@"addedItems"];
    [evtParams setObject:removedItems forKey:@"removedItems"];
    
    [evtParams setObject:removedUsers forKey:@"removedUsers"];
    [evtParams setObject:addedUsers forKey:@"addedUsers"];
    [evtParams setObject:mmoRoom forKey:@"room"];
    
    if ([_sfs.delegate respondsToSelector:@selector(onProximityListUpdate:)])
    {
        [_sfs.delegate onProximityListUpdate:[[SFSEvent alloc] initWithType:SFSEvent_PROXIMITY_LIST_UPDATE params:evtParams]];
    }
}

-(void) fnSetMMOItemVariables:(id<IMessage>)msg
{
    id <ISFSObject> sfso = [msg content];
    NSMutableDictionary *evtParams = [NSMutableDictionary dictionary];
    
    NSInteger roomId = [sfso getInt:SetMMOItemVariables_KEY_ROOM_ID];
    NSInteger mmoItemId = [sfso getInt:SetMMOItemVariables_KEY_ITEM_ID];
    id<ISFSArray> varList = [sfso getSFSArray:SetMMOItemVariables_KEY_VAR_LIST];
    
    MMORoom* mmoRoom = (MMORoom*) [_sfs getRoomById:roomId];
    NSMutableArray* changedVars = [[NSMutableArray alloc] init];
    
    if (mmoRoom)
    {
        id<IMMOItem> mmoItem = [mmoRoom getMMOItem:mmoItemId];
        
        if (mmoItem)
        {
            for (int i = 0; i < [varList size]; i++)
            {
                id<IMMOItemVariable> itemVar = [MMOItemVariable fromSFSArray:[varList getSFSArray:i]];
                [mmoItem setVariable:itemVar];
                
                [changedVars addObject:[itemVar name]];
            }
            
            [evtParams setObject:changedVars forKey:@"changedVars"];
            [evtParams setObject:mmoItem forKey:@"mmoItem"];
            [evtParams setObject:mmoRoom forKey:@"room"];
            
            if ([_sfs.delegate respondsToSelector:@selector(onMMOItemVariablesUpdate:)])
            {
                [_sfs.delegate onMMOItemVariablesUpdate:[[SFSEvent alloc] initWithType:SFSEvent_MMOITEM_VARIABLES_UPDATE params:evtParams]];
            }
        }
    }    
}

#pragma clang diagnostic pop
@end
