Example #1
0
int main (int argc, char ** argv) {
    prev = getthetime();
    (void) signal(SIGINT,leave);

    if (enet_initialize () != 0) {
        fprintf (stderr, "An error occurred while initializing ENet.\n");
        return EXIT_FAILURE;
    }

    atexit (enet_deinitialize);


    address.host = ENET_HOST_ANY;
    address.port = 5001;

    enetserver = enet_host_create (& address /* the address to bind the server host to */,
                                   32      /* allow up to 32 clients and/or outgoing connections */,
                                   0      /* allow up to 2 channels to be used, 0 and 1 */,
                                   0      /* assume any amount of incoming bandwidth */,
                                   0      /* assume any amount of outgoing bandwidth */);
    if (enetserver== NULL) {
        fprintf (stderr,
                 "An error occurred while trying to create an ENet server host.\n");
        exit (EXIT_FAILURE);
    }

    tronserver = server_init(enetserver);
    atexit(clean_up);
    printf("This is the TronClone Server.\nPush ^c to kill the server.\n");

    /* Main game loop */
    while(1) {
        int i, index, temp;
        char buf[80];
        if (enet_host_service (enetserver, & event, 10) > 0) {
            switch (event.type) {
            case ENET_EVENT_TYPE_CONNECT:
                enet_address_get_host_ip(&event.peer->address, buf, sizeof buf);
                printf ("A new client connected from %s\n",
                        buf);
                server_add_user(tronserver, event.peer);
                break;

            case ENET_EVENT_TYPE_RECEIVE:
                server_process_packet(tronserver, event);
                enet_packet_destroy (event.packet);
                break;

            case ENET_EVENT_TYPE_DISCONNECT:
                enet_address_get_host_ip(&event.peer->address, buf, sizeof buf);
                printf ("A client disconnected from %s: %u.\n",
                        buf,
                        event.peer -> address.port);

                server_remove_user(tronserver, event.peer);
                event.peer -> data = NULL;
                break;
            }
        }

        now = getthetime();
        double h = (now-prev)/1000000.0f;
        server_update(tronserver, h);
        prev = now;
    }
    clean_up();
}
Example #2
0
/* \brief update enet state internally */
static int _nethckEnetUpdate(void)
{
   __NETHCKclient client;
   ENetEvent event;
   unsigned int packets = 0;
   TRACE(2);

   /* wait up to 1000 milliseconds for an event */
   while (enet_host_service(_NETHCKserver.enet, &event, 1000) > 0) {
      switch (event.type) {
         case ENET_EVENT_TYPE_CONNECT:
            printf("A new client connected from %x:%u.\n",
                  event.peer->address.host,
                  event.peer->address.port);

            /* fill new client struct */
            memset(&client, 0, sizeof(__NETHCKclient));
            enet_address_get_host_ip(&event.peer->address, client.host, sizeof(client.host));
            event.peer->data = _nethckServerNewClient(&client);
            break;

         case ENET_EVENT_TYPE_RECEIVE:
            /* manage packet by kind */
            printf("A packet of length %zu was received on channel %u.\n",
                  event.packet->dataLength, event.channelID);
            printf("ID: %d\n", ((nethckPacket*)event.packet->data)->type);
            switch (((nethckPacket*)event.packet->data)->type) {
               case NETHCK_PACKET_OBJECT:
                  _nethckServerManagePacketObject(event.packet->data);
                  break;
               case NETHCK_PACKET_OBJECT_TRANSLATION:
               case NETHCK_PACKET_OBJECT_MATERIAL:
               case NETHCK_PACKET_OBJECT_TEXTURE:
               default:
                  break;
            }

            /* echo the packet to clients */
            enet_host_broadcast(_NETHCKserver.enet, 0, event.packet);

            /* clean up the packet now that we're done using it. */
            //enet_packet_destroy(event.packet);
            break;

         case ENET_EVENT_TYPE_DISCONNECT:
            printf("%s disconected.\n", ((__NETHCKclient*)event.peer->data)->host);

            /* free the client */
            _nethckServerFreeClient(event.peer->data);
            event.peer->data = NULL;
            break;

         default:
            break;
      }
      ++packets;
   }

   RET(2, "%u", packets);
   return packets;
}
Example #3
0
bool CNetServerWorker::RunStep()
{
	// Check for messages from the game thread.
	// (Do as little work as possible while the mutex is held open,
	// to avoid performance problems and deadlocks.)

	m_ScriptInterface->GetRuntime()->MaybeIncrementalGC(0.5f);

	JSContext* cx = m_ScriptInterface->GetContext();
	JSAutoRequest rq(cx);

	std::vector<std::pair<int, CStr> > newAssignPlayer;
	std::vector<bool> newStartGame;
	std::vector<std::pair<CStr, int> > newPlayerReady;
	std::vector<bool> newPlayerResetReady;
	std::vector<std::string> newGameAttributes;
	std::vector<u32> newTurnLength;

	{
		CScopeLock lock(m_WorkerMutex);

		if (m_Shutdown)
			return false;

		newStartGame.swap(m_StartGameQueue);
		newPlayerReady.swap(m_PlayerReadyQueue);
		newPlayerResetReady.swap(m_PlayerResetReadyQueue);
		newAssignPlayer.swap(m_AssignPlayerQueue);
		newGameAttributes.swap(m_GameAttributesQueue);
		newTurnLength.swap(m_TurnLengthQueue);
	}

	for (size_t i = 0; i < newAssignPlayer.size(); ++i)
		AssignPlayer(newAssignPlayer[i].first, newAssignPlayer[i].second);

	for (size_t i = 0; i < newPlayerReady.size(); ++i)
		SetPlayerReady(newPlayerReady[i].first, newPlayerReady[i].second);

	if (!newPlayerResetReady.empty())
		ClearAllPlayerReady();

	if (!newGameAttributes.empty())
	{
		JS::RootedValue gameAttributesVal(cx);
		GetScriptInterface().ParseJSON(newGameAttributes.back(), &gameAttributesVal);
		UpdateGameAttributes(&gameAttributesVal);
	}

	if (!newTurnLength.empty())
		SetTurnLength(newTurnLength.back());

	// Do StartGame last, so we have the most up-to-date game attributes when we start
	if (!newStartGame.empty())
		StartGame();

	// Perform file transfers
	for (size_t i = 0; i < m_Sessions.size(); ++i)
		m_Sessions[i]->GetFileTransferer().Poll();

	// Process network events:

	ENetEvent event;
	int status = enet_host_service(m_Host, &event, HOST_SERVICE_TIMEOUT);
	if (status < 0)
	{
		LOGERROR("CNetServerWorker: enet_host_service failed (%d)", status);
		// TODO: notify game that the server has shut down
		return false;
	}

	if (status == 0)
	{
		// Reached timeout with no events - try again
		return true;
	}

	// Process the event:

	switch (event.type)
	{
	case ENET_EVENT_TYPE_CONNECT:
	{
		// Report the client address
		char hostname[256] = "(error)";
		enet_address_get_host_ip(&event.peer->address, hostname, ARRAY_SIZE(hostname));
		LOGMESSAGE("Net server: Received connection from %s:%u", hostname, (unsigned int)event.peer->address.port);

		// Set up a session object for this peer

		CNetServerSession* session = new CNetServerSession(*this, event.peer);

		m_Sessions.push_back(session);

		SetupSession(session);

		ENSURE(event.peer->data == NULL);
		event.peer->data = session;

		HandleConnect(session);

		break;
	}

	case ENET_EVENT_TYPE_DISCONNECT:
	{
		// If there is an active session with this peer, then reset and delete it

		CNetServerSession* session = static_cast<CNetServerSession*>(event.peer->data);
		if (session)
		{
			LOGMESSAGE("Net server: Disconnected %s", DebugName(session).c_str());

			// Remove the session first, so we won't send player-update messages to it
			// when updating the FSM
			m_Sessions.erase(remove(m_Sessions.begin(), m_Sessions.end(), session), m_Sessions.end());

			session->Update((uint)NMT_CONNECTION_LOST, NULL);

			delete session;
			event.peer->data = NULL;
		}

		break;
	}

	case ENET_EVENT_TYPE_RECEIVE:
	{
		// If there is an active session with this peer, then process the message

		CNetServerSession* session = static_cast<CNetServerSession*>(event.peer->data);
		if (session)
		{
			// Create message from raw data
			CNetMessage* msg = CNetMessageFactory::CreateMessage(event.packet->data, event.packet->dataLength, GetScriptInterface());
			if (msg)
			{
				LOGMESSAGE("Net server: Received message %s of size %lu from %s", msg->ToString().c_str(), (unsigned long)msg->GetSerializedLength(), DebugName(session).c_str());

				HandleMessageReceive(msg, session);

				delete msg;
			}
		}

		// Done using the packet
		enet_packet_destroy(event.packet);

		break;
	}

	case ENET_EVENT_TYPE_NONE:
		break;
	}

	return true;
}
Example #4
0
{
    return curpeer || (attempt && connpeer);
}

ICOMMAND(isconnected, "i", (int *attempt), intret(isconnected(*attempt > 0) ? 1 : 0));

const ENetAddress *connectedpeer()
{
    return curpeer ? &curpeer->address : NULL;
}

ICOMMAND(connectedip, "", (),
{
    const ENetAddress *address = connectedpeer();
    string hostname;
    result(address && enet_address_get_host_ip(address, hostname, sizeof(hostname)) >= 0 ? hostname : "");
});

ICOMMAND(connectedport, "", (),
{
    const ENetAddress *address = connectedpeer();
    intret(address ? address->port : -1);
});

void abortconnect()
{
    if(!connpeer) return;
    game::connectfail();
    if(connpeer->state!=ENET_PEER_STATE_DISCONNECTED) enet_peer_reset(connpeer);
    connpeer = NULL;
    if(curpeer) return;
Example #5
0
VALUE renet_server_update(VALUE self, VALUE timeout)
{
  Server* server;
  Data_Get_Struct(self, Server, server);
  VALUE lock = rb_iv_get(self, "@lock");
  rb_mutex_lock(lock);
  int peer_id;
  
  /* wait up to timeout milliseconds for a packet */
  if (service(self, server, NUM2UINT(timeout)) > 0)
  {
    do
    {
      switch (server->event->type)
      {
        case ENET_EVENT_TYPE_NONE:
          break;
        
        case ENET_EVENT_TYPE_CONNECT:
          server->n_clients += 1;
          enet_address_get_host_ip(&(server->event->peer->address), server->conn_ip, 20);
          peer_id = (int)(server->event->peer - server->host->peers);
          renet_server_execute_on_connection(self, INT2NUM(peer_id), rb_str_new2(server->conn_ip));
          break;

        case ENET_EVENT_TYPE_RECEIVE:
          peer_id = (int)(server->event->peer - server->host->peers);
          renet_server_execute_on_packet_receive(self, INT2NUM(peer_id), server->event->packet, server->event->channelID);
          break;
       
        case ENET_EVENT_TYPE_DISCONNECT:
          server->n_clients -= 1;
          peer_id = (int)(server->event->peer - server->host->peers);
          server->event->peer->data = NULL;
          renet_server_execute_on_disconnection(self, INT2NUM(peer_id));
          break;
      }
    }
    while (service(self, server, 0) > 0);
  }

  /* we are unlocking now because it's important to unlock before going 
    back into ruby land (which rb_funcall will do). If we don't then an
    exception can leave the locks in an inconsistent state */
  rb_mutex_unlock(lock);

  {
    VALUE total = rb_iv_get(self, "@total_sent_data");
    VALUE result = rb_funcall( total
                             , rb_intern("+")
                             , 1
                             , UINT2NUM(server->host->totalSentData));
    rb_iv_set(self, "@total_sent_data", result); 
    server->host->totalSentData = 0;
  }

  {
    VALUE total = rb_iv_get(self, "@total_received_data");
    VALUE result = rb_funcall( total
                             , rb_intern("+")
                             , 1
                             , UINT2NUM(server->host->totalReceivedData));
    rb_iv_set(self, "@total_received_data", result);
    server->host->totalReceivedData = 0;
  }

  {
    VALUE total = rb_iv_get(self, "@total_sent_packets");
    VALUE result = rb_funcall( total
                             , rb_intern("+")
                             , 1
                             , UINT2NUM(server->host->totalSentPackets));
    rb_iv_set(self, "@total_sent_packets", result);
    server->host->totalSentPackets = 0;
  }

  {
    VALUE total = rb_iv_get(self, "@total_received_packets");
    VALUE result = rb_funcall( total
                             , rb_intern("+")
                             , 1
                             , UINT2NUM(server->host->totalReceivedPackets));
    rb_iv_set(self, "@total_received_packets", result);
    server->host->totalReceivedPackets = 0;
  }
  
  return Qtrue;
}
Example #6
0
void Room::RoomImpl::HandleModBanPacket(const ENetEvent* event) {
    if (!HasModPermission(event->peer)) {
        SendModPermissionDenied(event->peer);
        return;
    }

    Packet packet;
    packet.Append(event->packet->data, event->packet->dataLength);
    packet.IgnoreBytes(sizeof(u8)); // Ignore the message type

    std::string nickname;
    packet >> nickname;

    std::string username;
    std::string ip;

    {
        std::lock_guard lock(member_mutex);
        const auto target_member =
            std::find_if(members.begin(), members.end(),
                         [&nickname](const auto& member) { return member.nickname == nickname; });
        if (target_member == members.end()) {
            SendModNoSuchUser(event->peer);
            return;
        }

        // Notify the banned member
        SendUserBanned(target_member->peer);

        nickname = target_member->nickname;
        username = target_member->user_data.username;

        char ip_raw[256];
        enet_address_get_host_ip(&target_member->peer->address, ip_raw, sizeof(ip_raw) - 1);
        ip = ip_raw;

        enet_peer_disconnect(target_member->peer, 0);
        members.erase(target_member);
    }

    {
        std::lock_guard lock(ban_list_mutex);

        if (!username.empty()) {
            // Ban the forum username
            if (std::find(username_ban_list.begin(), username_ban_list.end(), username) ==
                username_ban_list.end()) {

                username_ban_list.emplace_back(username);
            }
        }

        // Ban the member's IP as well
        if (std::find(ip_ban_list.begin(), ip_ban_list.end(), ip) == ip_ban_list.end()) {
            ip_ban_list.emplace_back(ip);
        }
    }

    // Announce the change to all clients.
    SendStatusMessage(IdMemberBanned, nickname, username);
    BroadcastRoomInformation();
}
Example #7
0
void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
    {
        std::lock_guard lock(member_mutex);
        if (members.size() >= room_information.member_slots) {
            SendRoomIsFull(event->peer);
            return;
        }
    }
    Packet packet;
    packet.Append(event->packet->data, event->packet->dataLength);
    packet.IgnoreBytes(sizeof(u8)); // Ignore the message type
    std::string nickname;
    packet >> nickname;

    std::string console_id_hash;
    packet >> console_id_hash;

    MacAddress preferred_mac;
    packet >> preferred_mac;

    u32 client_version;
    packet >> client_version;

    std::string pass;
    packet >> pass;

    std::string token;
    packet >> token;

    if (pass != password) {
        SendWrongPassword(event->peer);
        return;
    }

    if (!IsValidNickname(nickname)) {
        SendNameCollision(event->peer);
        return;
    }

    if (preferred_mac != NoPreferredMac) {
        // Verify if the preferred mac is available
        if (!IsValidMacAddress(preferred_mac)) {
            SendMacCollision(event->peer);
            return;
        }
    } else {
        // Assign a MAC address of this client automatically
        preferred_mac = GenerateMacAddress();
    }

    if (!IsValidConsoleId(console_id_hash)) {
        SendConsoleIdCollision(event->peer);
        return;
    }

    if (client_version != network_version) {
        SendVersionMismatch(event->peer);
        return;
    }

    // At this point the client is ready to be added to the room.
    Member member{};
    member.mac_address = preferred_mac;
    member.console_id_hash = console_id_hash;
    member.nickname = nickname;
    member.peer = event->peer;

    std::string uid;
    {
        std::lock_guard lock(verify_UID_mutex);
        uid = verify_UID;
    }
    member.user_data = verify_backend->LoadUserData(uid, token);

    {
        std::lock_guard lock(ban_list_mutex);

        // Check username ban
        if (!member.user_data.username.empty() &&
            std::find(username_ban_list.begin(), username_ban_list.end(),
                      member.user_data.username) != username_ban_list.end()) {

            SendUserBanned(event->peer);
            return;
        }

        // Check IP ban
        char ip_raw[256];
        enet_address_get_host_ip(&event->peer->address, ip_raw, sizeof(ip_raw) - 1);
        std::string ip = ip_raw;

        if (std::find(ip_ban_list.begin(), ip_ban_list.end(), ip) != ip_ban_list.end()) {
            SendUserBanned(event->peer);
            return;
        }
    }

    // Notify everyone that the user has joined.
    SendStatusMessage(IdMemberJoin, member.nickname, member.user_data.username);

    {
        std::lock_guard lock(member_mutex);
        members.push_back(std::move(member));
    }

    // Notify everyone that the room information has changed.
    BroadcastRoomInformation();
    if (HasModPermission(event->peer)) {
        SendJoinSuccessAsMod(event->peer, preferred_mac);
    } else {
        SendJoinSuccess(event->peer, preferred_mac);
    }
}
Example #8
0
void NetServer::ReadDriverInfoPacket(ENetPacket *pPacket, ENetPeer * pPeer)
{
	NetDriver driver;

	char hostName[256];
	enet_address_get_host_ip (&driver.address,hostName,256);

    GfLogTrace ("Client Player Info connected from %s\n",hostName); 

        PackedBuffer msg(pPacket->data, pPacket->dataLength);
        GfLogTrace("ReadDriverInfoPacket: packed data length=%d\n",
                msg.length());

        try
        {
                msg.unpack_ubyte();
                driver.idx = msg.unpack_int();
                msg.unpack_string(driver.name, sizeof driver.name);
                msg.unpack_string(driver.car, sizeof driver.car);
                msg.unpack_string(driver.team, sizeof driver.team);
                msg.unpack_string(driver.author, sizeof driver.author);
                driver.racenumber = msg.unpack_int();
                msg.unpack_string(driver.skilllevel, sizeof driver.skilllevel);
                driver.red = msg.unpack_float();
                driver.green = msg.unpack_float();
                driver.blue = msg.unpack_float();
                msg.unpack_string(driver.module, sizeof driver.module);
                msg.unpack_string(driver.type, sizeof driver.type);
                driver.client = msg.unpack_int();
        }
        catch (PackedBufferException &e)
        {
                GfLogFatal("ReadDriverInfoPacket: packed buffer error\n");
        }

	GfLogTrace("ReadDriverInfoPacket: driver\n");
	GfLogTrace(".host=%d\n", driver.address.host);
	GfLogTrace(".port=%d\n", driver.address.port);
	GfLogTrace(".idx=%d\n", driver.idx);
	GfLogTrace(".name=%s\n", driver.name);
	GfLogTrace(".car=%s\n", driver.car);
	GfLogTrace(".team=%s\n", driver.team);
	GfLogTrace(".author=%s\n", driver.author);
	GfLogTrace(".racenumber=%d\n", driver.racenumber);
	GfLogTrace(".skilllevel=%s\n", driver.skilllevel);
	GfLogTrace(".red=%.1f\n", driver.red);
	GfLogTrace(".green=%.1f\n", driver.green);
	GfLogTrace(".blue=%.1f\n", driver.blue);
	GfLogTrace(".module=%s\n", driver.module);
	GfLogTrace(".type=%s\n", driver.type);
	GfLogTrace(".client=%d\n", driver.client);

	//Make sure player name is unique otherwise disconnect player
	NetServerMutexData *pSData = LockServerData();
	for(unsigned int i=0;i<pSData->m_vecNetworkPlayers.size();i++)
	{
		if (strcmp(driver.name,pSData->m_vecNetworkPlayers[i].name)==0)
		{
			SendPlayerRejectedPacket(pPeer,"Player name already used. Please choose a different name.");
			UnlockServerData();
			return;
		}
	}
	UnlockServerData();

	driver.address.host = pPeer->address.host;
	driver.hostPort = pPeer->address.port;

	SendPlayerAcceptedPacket(pPeer);
	UpdateDriver(driver);

	GfLogTrace("Reading Driver Info Packet:  Driver: %s,Car: %s\n",driver.name,driver.car);
}
Example #9
0
void NetServer::RemoveDriver(ENetEvent event)
{
	int playerStartIndex;
	ENetAddress address = event.peer->address;

	char hostName[256];
	enet_address_get_host_ip (&address,hostName,256);

    GfLogTrace ("Client Player Info disconnect from %s\n",hostName); 
	
	std::vector<NetDriver>::iterator p;

	if (m_vecWaitForPlayers.size()>0)
	{
		p = m_vecWaitForPlayers.begin();

		while(p!=m_vecWaitForPlayers.end())
		{

			if ((p->address.host == address.host)&&(p->hostPort == address.port))
			{
				m_vecWaitForPlayers.erase(p);
				break;
			}

			p++;
		}

		if (m_vecWaitForPlayers.size()==0)
			m_bBeginRace = true;
	}

	//look for driver id
	NetServerMutexData *pSData = LockServerData();
	for (p = pSData->m_vecNetworkPlayers.begin();p!=pSData->m_vecNetworkPlayers.end();p++)
	{
		if (p->client)
		{
			if ((p->address.host == address.host)&&(p->hostPort == address.port))
			{
				if(m_bRaceActive)
				{
					playerStartIndex = p->idx-1;
					pSData->m_vecNetworkPlayers.erase(p);
					RemovePlayerFromRace(playerStartIndex);
					GenerateDriversForXML();
					RobotXml rXml;
					rXml.CreateRobotFile("networkhuman",pSData->m_vecNetworkPlayers);
					SetRaceInfoChanged(true);
				}
				else
				{
					pSData->m_vecNetworkPlayers.erase(p);
					GenerateDriversForXML();
					RobotXml rXml;
					rXml.CreateRobotFile("networkhuman",pSData->m_vecNetworkPlayers);
					SetRaceInfoChanged(true);
				}

				UnlockServerData();
				return;
			}
		}
	}

	UnlockServerData();
}
Example #10
0
int main (int argc, char *argv[])
{
    ENetAddress address;
    ENetEvent event;
    ChatContext cc, *pcc = &cc;
    char host_ip[32] = {0};
    char peer_info[32] = {0};
    int lastEvent = -1;
    int enableDebug = 0;
    int trycount = 0;
    int ret;

    memset(pcc, 0, sizeof(*pcc));

    /* Initialize the ENet */
    if (enet_initialize() != 0) {
        fprintf(stderr, "An error (%s) occured while initializing ENet.\n", strerror(errno));
        return EXIT_FAILURE;
    }
    atexit(enet_deinitialize);

    /* Create the client host */
    pcc->client = enet_host_create(NULL, SHUTTLE_MAX_CLIENT_NUM, SHUTTLE_MAX_CHANNEL_NUM,
                                   SHUTTLE_MAX_INCOMING_BANDWIDTH, SHUTTLE_MAX_OUTGOING_BANDWIDTH);
    if (pcc->client == NULL) {
        fprintf(stderr, "An error (%s) occured while trying to create an ENet client host.\n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    /* Connect to the server */
    enet_address_set_host(&address, SHUTTLE_SERVER_HOST);
    address.port = SHUTTLE_SERVER_PORT;
    pcc->peer = enet_host_connect(pcc->client, &address, SHUTTLE_MAX_CHANNEL_NUM, 0);
    if (pcc->peer == NULL) {
        fprintf(stderr, "No available peers for initializing an ENet connection.\n");
        exit(EXIT_FAILURE);
    }

    do {
        trycount++;
        printf("(Peer) Try to connect to server: the %dth tryouts.\n", trycount);
        if (enet_host_service(pcc->client, &event, 1000) > 0 && event.type == ENET_EVENT_TYPE_CONNECT) {
            /* We can send packet to server only after we have received ENET_EVENT_TYPE_CONNECT */
            pcc->connected = 1;
            enet_address_get_host_ip(&event.peer->address, host_ip, sizeof(host_ip) - 1);
            snprintf(peer_info, sizeof(peer_info), "[%s:%d]", host_ip, event.peer->address.port);
            if (event.peer->data)
                free(event.peer->data);
            event.peer->data = malloc(strlen(peer_info) + 1);
            if (event.peer->data)
                strcpy(event.peer->data, peer_info);
            printf("(Peer) Connected to server (%s:%d).\n", host_ip, event.peer->address.port);
        }
    } while (trycount < 4 && !pcc->connected);

    if (!pcc->connected) {
        fprintf(stderr, "Fail to connect to server.\n");
        enet_peer_reset(pcc->peer);
        enet_host_destroy(pcc->client);
        exit(EXIT_FAILURE);
    }

    /* We do not block the main event dispatcher */
    ret = pthread_create(&pcc->thread, NULL, peer_chater, pcc);
    if (ret) {
        pcc->thread = 0;
        fprintf(stderr, "Fail to create thread.\n");
        goto cleanup_pos;
    }

    while (1) {
        /* Event dispatcher: MUST not be hanged up */
        int eventStatus = enet_host_service(pcc->client, &event, 10);
        if (eventStatus >= 0) {
            switch (event.type) {
            case ENET_EVENT_TYPE_NONE:
                /* Silence huge repeated NONE events */
                if (lastEvent != ENET_EVENT_TYPE_NONE) {
                    if (enableDebug)
                        printf("(Peer) No event.\n");
                }
                break;
            case ENET_EVENT_TYPE_CONNECT:
                /* Store any relevant client information here. */
                pcc->connected = 1;
                enet_address_get_host_ip(&event.peer->address, host_ip, sizeof(host_ip) - 1);
                snprintf(peer_info, sizeof(peer_info), "[%s:%d]", host_ip, event.peer->address.port);
                if (event.peer->data)
                    free(event.peer->data);
                event.peer->data = malloc(strlen(peer_info));
                if (event.peer->data)
                    strcpy(event.peer->data, peer_info);
                printf("(Peer) Connected to server (%s:%d).\n", host_ip, event.peer->address.port);
                break;
            case ENET_EVENT_TYPE_RECEIVE:
                if (event.channelID == SHUTTLE_CHANNEL_NOTIFY)
                    printf("(Peer) Got a notification message : %s.\n", (char*)event.packet->data);
                else
                    printf("(Peer) Got a chat message: %s.\n", (char*)event.packet->data);
                /* Clean up the packet now that we're done using it. */
                enet_packet_destroy(event.packet);
                break;
            case ENET_EVENT_TYPE_DISCONNECT:
                /* A connected peer has either explicitly disconnected or timed out. */
                printf("(Peer) Connection status: %d.\n", pcc->connected);
                if (event.peer->data) {
                    printf("(Peer) %s is disconnected.\n", (char*)event.peer->data);
                    free(event.peer->data);
                } else {
                    /* We fail to receive CONNECT event becasue the server is down. */
                    enet_address_get_host_ip(&event.peer->address, host_ip, sizeof(host_ip) - 1);
                    snprintf(peer_info, sizeof(peer_info), "[%s:%d]", host_ip, event.peer->address.port);
                    printf("(Peer) Unknown (%s) connection is disconnected.\n", peer_info);
                }
                /* Reset the peer's information. */
                event.peer->data = NULL;
                pcc->connected = 0;
                lastEvent = -1;
                enet_peer_reset(event.peer);
                /* Reconnect the server */
                pcc->peer = enet_host_connect(pcc->client, &address, SHUTTLE_MAX_CHANNEL_NUM, 0);
                if (pcc->peer == NULL) {
                    fprintf(stderr, "No available peers for initializing an ENet connection.\n");
                    enet_host_destroy(pcc->client);
                    ret = EXIT_FAILURE;
                    goto cleanup_pos;
                }
                break;
            default:
                assert(0);
                break;
            }

            lastEvent = event.type;
        } else {
            fprintf(stderr, "(Peer) Something went wrong: %d.\n", eventStatus);
            lastEvent = -1;
            pcc->connected = 0;
            enet_peer_reset(pcc->peer);
            ret = eventStatus;
            goto cleanup_pos;
        }
    }

    ret = 0;

cleanup_pos:
    /* Terminate the chater thread. */
    pcc->terminated = 1;
    if (pcc->thread) {
        pthread_join(pcc->thread, NULL);
        pcc->thread = 0;
    }

    enet_host_destroy(pcc->client);
    printf("Client is terminated.\n");

    return ret;
}