//---------------------------------------------------------------------------------------------------- /// <summary> /// 클라이언트 추가 /// </summary> /// <param name="client">클라이언트 객체</param> //---------------------------------------------------------------------------------------------------- public bool AddClient( cClient client ) { if( client != null ) { if( Children.ContainsKey( client.ClientID ) ) return true; if( AddChild( client.ClientID, client ) ) { client.Channel = ChannelID; return true; } } return false; }
//---------------------------------------------------------------------------------------------------- /// <summary> /// 클라이언트 삭제 /// </summary> /// <param name="client">클라이언트 객체</param> //---------------------------------------------------------------------------------------------------- public void RemoveClient( cClient client ) { if( client != null ) { RemoveChild( client.ClientID ); client.Channel = (byte)cChannel.NULL_ID; } }
//---------------------------------------------------------------------------------------------------- /// <summary> /// 수신된 데이터를 파싱한다. /// 주의 : 이 함수는 상속 받아서 클라이언트마다 각자 프로토콜을 정의해서 사용한다. /// </summary> /// <param name="client">수신 클라이언트</param> /// <param name="data">수신 데이터</param> /// <param name="size">수신 데이터 크기</param> //---------------------------------------------------------------------------------------------------- protected virtual void OnRecv( cClient client, byte[] data, int size ) { cBitStream bits = new cBitStream(); bits.Write( data, 0, size ); Print( bits.ToString() ); eOrder order = ReadOrder( bits ); eResult result = ReadResult( bits ); Print( order + " : '"+client.Name+"'" ); // 파싱 switch( order ) { case eOrder.OBJECT_UPDATE: RecvObjectUpdate( client, result, bits ); break; default: Error( client.Name + ":" + order ); break; } }
//---------------------------------------------------------------------------------------------------- /// <summary> /// 동기화 오브젝트를 파싱. /// </summary> /// <param name="client">수신 클라이언트</param> /// <param name="result">결과값</param> /// <param name="bits">수신 데이터</param> //---------------------------------------------------------------------------------------------------- public void RecvObjectUpdate( cClient client, eResult result, cBitStream bits ) { bool host; bits.Read( out host ); bool cobject; bits.Read( out cobject ); uint object_id; bits.Read( out object_id, uint.MaxValue ); if( cobject ) { cObject value; if( m_sync_cobject.TryGetValue( object_id, out value ) ) { Type ctype = value.GetType(); foreach( FieldInfo field in ctype.GetFields(BindingFlags.NonPublic|BindingFlags.Public) ) { if( host ) { HOSTAttribute attrib=GetHostAttribute( field ); if( attrib != null ) { object data = field.GetValue(value); bits.Read( field.GetType(), out data, data, attrib.MaxSize, attrib.MaxPoint ); } } else { GUESTAttribute attrib=GetGuestAttribute( field ); if( attrib != null ) { object data = field.GetValue(value); bits.Read( field.GetType(), out data, data, attrib.MaxSize, attrib.MaxPoint ); } } } } } else { cUnityObject value; if( cUnityObject.GetInstance( object_id, out value ) ) { Type ctype = value.GetType(); foreach( FieldInfo field in ctype.GetFields(BindingFlags.NonPublic|BindingFlags.Public) ) { if( host ) { HOSTAttribute attrib=GetHostAttribute( field ); if( attrib != null ) { object data = field.GetValue(value); bits.Read( field.GetType(), out data, data, attrib.MaxSize, attrib.MaxPoint ); } } else { GUESTAttribute attrib=GetGuestAttribute( field ); if( attrib != null ) { object data = field.GetValue(value); bits.Read( field.GetType(), out data, data, attrib.MaxSize, attrib.MaxPoint ); } } } } } }
//---------------------------------------------------------------------------------------------------- /// <summary> /// 어플리케이션 종료. /// 주의: 프로그램 종료될때 메인 프로세스에서 호출해줘야 한다. /// </summary> //---------------------------------------------------------------------------------------------------- public virtual void OnApplicationQuit() { if( s_client != null ) { s_client.Disconnect(); s_client = null; } if( s_thread_connect!=null ) { s_thread_connect.Abort(); s_thread_connect = null; } }
//---------------------------------------------------------------------------------------------------- /// <summary> /// 클라이언트 접속 종료 /// </summary> /// <param name="client"></param> //---------------------------------------------------------------------------------------------------- public void DisconnectClient( cClient client ) { if( client != null ) { OutChannel( client.Channel, client ); RemoveClient( client.ClientID ); } }
//---------------------------------------------------------------------------------------------------- /// <summary> /// 파티 퇴장. /// </summary> /// <param name="party_id">파티 아이디.</param> /// <param name="client">클라이언트 객체.</param> //---------------------------------------------------------------------------------------------------- public void OutParty( uint party_id, cClient client ) { lock(m_parties) { cParty party; if( m_parties.TryGetValue( party_id, out party ) ) { party.RemoveChild( client.ClientID ); client.Party = cParty.NULL_ID; if( party.Children.Count == 0 ) { m_parties.Remove( party_id ); } else { // 파티장 교체 : 첫번째 유저 if( client.Master ) { foreach( KeyValuePair<uint,cObject> entry in party.Children ) { cClient new_master=(cClient)entry.Value; new_master.Master = true; break; } } } } } }
//---------------------------------------------------------------------------------------------------- /// <summary> /// 클라이언트 추가. /// </summary> /// <param name="id">클라이언트 아이디.</param> /// <param name="value">클라이언트 인스턴스.</param> //---------------------------------------------------------------------------------------------------- public void AddClient( uint id, cClient value ) { lock(s_clients) { s_clients.Add( id, value ); } }
//---------------------------------------------------------------------------------------------------- /// <summary> /// 스테이지 입장.(추가+입장) /// </summary> /// <param name="stage_id">스테이지 아이디.</param> /// <param name="client">클라이언트 객체.</param> /// <param name="max_user">스테이지 최대 입장객수.</param> /// <returns>파티 아이디</returns> //---------------------------------------------------------------------------------------------------- public uint InStage( uint stage_id, byte max_user, cClient client ) { lock(m_stages) { // 스테이지가 없으면 생성 cStage stage=null; if( m_stages.TryGetValue( stage_id, out stage )==false ) { stage = new cStage( stage_id ); m_stages.Add( stage_id, stage ); } // 파티중 빈 자리에 먼저 입장한다. cParty party=null; foreach( KeyValuePair<uint,cObject> entry in stage.Children ) { party = (cParty)entry.Value; // 파티 입장 if( InParty( party.PartyID, client ) ) { client.Stage = stage_id; return party.PartyID; } } // 빈자리가 없으면 새로 파티를 만든다. // 파티 생성 party = AddParty(max_user); if( party != null ) { // 파티 등록 if( stage.AddChild( party.PartyID, party ) ) { // 파티 입장 if( InParty( party.PartyID, client ) ) { client.Stage = stage_id; return party.PartyID; } } } } return cParty.NULL_ID; }
//---------------------------------------------------------------------------------------------------- /// <summary> /// 채널 퇴장. /// </summary> /// <param name="channel_id">채널 아이디.</param> /// <param name="client">클라이언트 객체.</param> //---------------------------------------------------------------------------------------------------- public void OutChannel( byte channel_id, cClient client ) { // 스테이지 퇴장 OutStage( client.Stage, client ); // 채널 퇴장 lock(m_channels) { cChannel channel; if( m_channels.TryGetValue( channel_id, out channel ) ) { channel.RemoveClient( client ); //----------------------------------------------- // Response cBitStream bits = new cBitStream(); WriteOrder( bits, cNetwork.eOrder.CHANNEL_OUT ); WriteResult( bits, eResult.SUCCESS ); WriteClientId( bits, client.ClientID ); client.Send( bits ); SendChannel( channel_id, bits ); } } }
//---------------------------------------------------------------------------------------------------- /// <summary> /// 파티 입장. /// </summary> /// <param name="party_id">파티 아이디.</param> /// <param name="client">클라이언트 객체.</param> /// <returns>결과</returns> //---------------------------------------------------------------------------------------------------- public bool InParty( uint party_id, cClient client ) { lock(m_parties) { cParty party; if( m_parties.TryGetValue( party_id, out party ) ) { if( party.LockIn==false ) { if( party.AddChild( client.ClientID, client ) ) { int count = party.Children.Count; client.Master = (count==1); client.Party = party_id; if( count >= party.MaxUser ) { party.LockIn = true; } return true; } } } } return false; }
//---------------------------------------------------------------------------------------------------- /// <summary> /// 채널 입장. /// </summary> /// <param name="channel_id">채널 아이디.</param> /// <param name="client">클라이언트 객체.</param> /// <returns>결과</returns> //---------------------------------------------------------------------------------------------------- public bool InChannel( byte channel_id, cClient client ) { lock(m_channels) { cChannel channel; if( m_channels.TryGetValue( channel_id, out channel ) ) { return channel.AddClient( client ); } } return false; }
//---------------------------------------------------------------------------------------------------- /// <summary> /// 클라이언트를 얻어온다. /// </summary> /// <param name="client_id">클라이언트 아이디.</param> /// <param name="value">클라이언트 객체.</param> /// <returns>성공 유무.</returns> //---------------------------------------------------------------------------------------------------- public bool GetClient( uint client_id, out cClient value ) { lock(m_clients) { return m_clients.TryGetValue( client_id, out value ); } }
//---------------------------------------------------------------------------------------------------- /// <summary> /// 데이터 수신 처리 /// </summary> /// <param name="client">cClient 인스턴스</param> /// <param name="data">수신 데이터</param> /// <param name="size">수신 데이터 크기</param> //---------------------------------------------------------------------------------------------------- void OnRecv( cClient client, byte[] data, int size ) { if( client.UseCryptogram ) data = cCryptogram.Decrypt( data, size, client.DataIV, client.DataKey ); cBitStream bits = new cBitStream(); bits.Write( data, 0, size ); bits.Position = 0; cNetwork.eOrder order = cNetwork.ReadOrder( bits ); cNetwork.eResult result = cNetwork.ReadResult( bits ); if( result != cNetwork.eResult.SUCCESS ) { Error( order + " : " + result + " : \n" + bits.ToString() ); } else { Print( order + " : " + result + " : \n" + bits.ToString() ); } switch( order ) { case cNetwork.eOrder.SERVER_LOGIN: RecvServerLogin( result, bits ); break; case cNetwork.eOrder.SERVER_IN: RecvServerIn( result, bits ); break; case cNetwork.eOrder.SERVER_OUT: RecvServerOut( result, bits ); break; case cNetwork.eOrder.CLIENT_INFO_DEFAULT: RecvClientInfoDefault( result, bits ); break; case cNetwork.eOrder.CHANNEL_LIST: RecvChannelList( result, bits ); break; case cNetwork.eOrder.CHANNEL_IN: RecvChannelIn( result, bits ); break; case cNetwork.eOrder.CHANNEL_OUT: RecvChannelOut( result, bits ); break; case cNetwork.eOrder.CHANNEL_CHAT: RecvChannelChat( result, bits ); break; case cNetwork.eOrder.PARTY_CHAT: RecvPartyChat( result, bits ); break; case cNetwork.eOrder.STAGE_LIST: RecvStageList( result, bits ); break; case cNetwork.eOrder.STAGE_IN_REQUEST: RecvStageInRequest( result, bits ); break; case cNetwork.eOrder.STAGE_IN_ACCEPT: RecvStageInAccept( result, bits ); break; case cNetwork.eOrder.STAGE_USER_IN: RecvStageUserIn( result, bits ); break; case cNetwork.eOrder.STAGE_USER_OUT: RecvStageUserOut( result, bits ); break; case cNetwork.eOrder.STAGE_USER_MOVE: RecvStageUserMove( result, bits ); break; case cNetwork.eOrder.STAGE_USER_ATTACK_MONSTER: RecvStageUserAttackMonster( result, bits ); break; case cNetwork.eOrder.STAGE_USER_SKILL_SELF: RecvStageUserSkillSelf( result, bits ); break; case cNetwork.eOrder.STAGE_USER_SKILL_MONSTER: RecvStageUserSkillMonster( result, bits ); break; case cNetwork.eOrder.STAGE_USER_SKILL_POS: RecvStageUserSkillPos( result, bits ); break; case cNetwork.eOrder.STAGE_USER_DAMAGE: RecvStageUserDemage( result, bits ); break; case cNetwork.eOrder.STAGE_USER_ITEM_USE_SELF: RecvStageUserItemUseSelf( result, bits ); break; case cNetwork.eOrder.STAGE_USER_TRIGGER_ON: RecvStageUserTriggerOn( result, bits ); break; case cNetwork.eOrder.STAGE_MON_IN: RecvStageMonIn( result, bits ); break; case cNetwork.eOrder.STAGE_MON_MOVE: RecvStageMonMove( result, bits ); break; case cNetwork.eOrder.STAGE_MON_ATTACK_USER: RecvStageMonAttackUser( result, bits ); break; case cNetwork.eOrder.STAGE_MON_SKILL_SELF: RecvStageMonSkillSelf( result, bits ); break; case cNetwork.eOrder.STAGE_MON_SKILL_ACTOR: RecvStageMonSkillUser( result, bits ); break; case cNetwork.eOrder.STAGE_MON_SKILL_POS: RecvStageMonSkillPos( result, bits ); break; case cNetwork.eOrder.STAGE_MON_DAMAGE: RecvStageMonDemage( result, bits ); break; case cNetwork.eOrder.STAGE_DATA: RecvStageData( result, bits ); break; default: client.Disconnect(); break; } }
//---------------------------------------------------------------------------------------------------- /// <summary> /// 스테이지 퇴장. /// </summary> /// <param name="stage_id">스테이지 아이디.</param> /// <param name="client">클라이언트 객체.</param> //---------------------------------------------------------------------------------------------------- public void OutStage( uint stage_id, cClient client ) { lock(m_stages) { // 스테이지 퇴장 cStage stage=null; if( m_stages.TryGetValue( stage_id, out stage ) ) { client.Stage = cStage.NULL_ID; } // 파티 퇴장 uint party_id = client.Party; if( party_id != cParty.NULL_ID ) { lock(m_parties) { // 파티 퇴장 OutParty( party_id, client ); // 등록된 파티 제거 if( IsParty( party_id )==false ) { if( stage != null ) { stage.RemoveChild( party_id ); } } //----------------------------------------------- // Response cBitStream bits = new cBitStream(); WriteOrder( bits, cNetwork.eOrder.STAGE_USER_OUT ); WriteResult( bits, eResult.SUCCESS ); WriteClientId( bits, client.ClientID ); WriteClientId( bits, GetPartyMaster( party_id ) ); client.Send( bits ); SendParty( party_id, bits ); } } } }
//---------------------------------------------------------------------------------------------------- /// <summary> /// 생성자 /// </summary> //---------------------------------------------------------------------------------------------------- public cNetConnector( string version, string address, ushort port, ushort recv_buf_size, bool use_cryptogram ) : base("NC") { s_version = version; s_server_address = address; s_server_port = port; s_recv_buf_size = recv_buf_size; s_use_cryptogram = use_cryptogram; s_policy_port = 843; s_mutex = new Mutex(true); s_client = new cClient( cClient.NULL_ID, recv_buf_size, use_cryptogram, DoRecv ); s_clients = new Dictionary<uint,cClient>(); s_channels = new Dictionary<byte,cChannel>(); s_stage_party_count = new Dictionary<uint,ushort>(); s_thread_connect = new Thread(ThreadConnect); s_thread_connect.Start(this); }
//---------------------------------------------------------------------------------------------------- /// <summary> /// 리슨 프로세스 /// </summary> //---------------------------------------------------------------------------------------------------- protected virtual void DoListen() { try { m_listener = new TcpListener( System.Net.IPAddress.Any, m_port ); m_listener.Start(); while(true) { Print( "WAIT_CLIENT" ); cClient client = new cClient( cClient.UniqueID, m_listener.AcceptTcpClient(), RecvBufSize, UseCryptogram, OnRecv ); client.Parent = this; Print( "CONNECT : " + client.Address ); } } catch( Exception ex ) { Error( ex.Message ); Log( "error:"+ex ); } }
//---------------------------------------------------------------------------------------------------- /// <summary> /// 클라이언트를 얻어온다. /// </summary> /// <param name="id">클라이언트 아이디.</param> /// <param name="value">클라이언트 객체.</param> /// <returns>성공 유무.</returns> //---------------------------------------------------------------------------------------------------- public bool GetClient( uint id, out cClient value ) { lock(s_clients) { return s_clients.TryGetValue( id, out value ); } }
//---------------------------------------------------------------------------------------------------- /// <summary> /// 클라이언트로 부터 받은 패킷처리 /// 주의 : 이 함수를 상속받아서 수신 데이터 처리를 요함 /// </summary> /// <param name="client"></param> /// <param name="data"></param> /// <param name="size"></param> protected virtual void OnRecv( cClient client, byte[] data, int size ) { }
//---------------------------------------------------------------------------------------------------- /// <summary> /// 데이터 수신 처리 /// </summary> /// <param name="client">cClient 인스턴스</param> /// <param name="data">수신 데이터</param> /// <param name="size">수신 데이터 크기</param> //---------------------------------------------------------------------------------------------------- protected void DoRecv( cClient client, byte[] data, int size ) { // 메인쓰래드 작업이 종료될때까지 기다린다. s_mutex.WaitOne(); // 받은 데이터를 처리한다. OnRecv( client, data, size ); // 다른쓰래드로 프로세스를 넘긴다. s_mutex.ReleaseMutex(); }
//---------------------------------------------------------------------------------------------------- /// <summary> /// 클라이언트 추가. /// </summary> /// <param name="client_id">클라이언트 아이디.</param> /// <param name="value">클라이언트 인스턴스.</param> //---------------------------------------------------------------------------------------------------- public void AddClient( uint client_id, cClient value ) { lock(m_clients) { m_clients.Add( client_id, value ); } }