본문 바로가기
GameMaker강좌[GMS2]/네트워크강좌

[게임메이커 강좌-네트워킹][GMS2] 채팅 프로그램 만들기-4-채팅 메시지 보내기

by 타락카얀 2024. 12. 24.
728x90

 

 

GAME MAKER 강좌

 

KAYAN

 

 

 

 

 

 

◈ 채팅 메시지 보내기

 

이제 본격적으로 서버와 클라이언트 간에 메시지를 주고 받아봅시다.

 

먼저, 이전에 채팅 테스트를 했던 obj_chat 오브젝트의 [Step 이벤트] 내용을 지웁니다.

그리고 서버 오브젝트에 [Step 이벤트]를 추가하고, 채팅 메시지를 접속한 클라이언트에게 보내도록 합니다.

 

 
obj_server 오브젝트 - Step 이벤트
 
 
if keyboard_check_pressed( vk_enter ){
keyboard_string = string_replace_all( keyboard_string, "\n", "" );
if string_length( keyboard_string )>100{ keyboard_string = string_copy( keyboard_string, 1, 100 ); }
 
if !( keyboard_string == "" ){
// == == ==
var t = "[서버관리자]["+string( global.playername )+"]"+keyboard_string;
ds_list_add( global.chat_list, t );
 
 
var i, sock;
for ( var i = 0; i < ds_list_size( global.socketlist ); i++; )
{
sock = ds_list_find_value( global.socketlist, i );
 
buffer_seek( global.net_buffer, buffer_seek_start, 0 );
buffer_write( global.net_buffer, buffer_u16, 5 ); //패킷 이벤트 번호
buffer_write( global.net_buffer, buffer_string, t );
network_send_packet( sock, global.net_buffer, buffer_tell( global.net_buffer ) );
}
 
// == == ==
keyboard_string = "";
 
}
 
}
 

 

채팅 메시지는 클라이언트의 5번 수신 이벤트로 보낼 겁니다.

그러면 클라이언트 오브젝트 [네트워크 비동기 이벤트]의 5번 수신 이벤트에서 패킷을 정리합니다.

 

 
obj_client 오브젝트 - Async - Networking 이벤트
 
 
var n_id = ds_map_find_value( async_load, "id" );
if n_id == global.client_socket{
 
//서버로 부터 받은 패킷 데이터
// == == == == == == == == == == == == == ==
var type = ds_map_find_value( async_load, "type" );
 
if ( type == network_type_data ){
 
var t_buffer = ds_map_find_value( async_load, "buffer" );
var cmd_type = buffer_read( t_buffer, buffer_u16 );
switch ( cmd_type ){
//--------- 클라이언트 세부 정보 갱신
case 1:
global.server_name = buffer_read( t_buffer, buffer_string );
var t = buffer_read( t_buffer, buffer_string );
 
ds_map_destroy( global.player_map );
global.player_map = json_decode( buffer_read( t_buffer, buffer_string ) ); //접속자들 정보
 
ds_list_add( global.chat_list, t );
 
break;
//--------- 클라이언트 세부 정보 갱신
 
//--------- 서버에 처음 접속했을 때
case 2:
var _id = buffer_read( t_buffer, buffer_u16 );
var t = buffer_read( t_buffer, buffer_string );
 
global.user_id = _id;
 
ds_list_add( global.chat_list, t );
break;
//--------- 서버에 처음 접속했을 때
 
//--------- ▼ 챗팅
case 5:
var t = buffer_read( t_buffer, buffer_string );
ds_list_add( global.chat_list, t );
break;
//--------- ▲ 챗팅
 
}
 
}
// == == == == == == == == == == == == == ==
}
 

 

버퍼를 읽고, 채팅글로 추가합니다.

 

 

이제 클라이언트도 채팅 메시지를 서버로 보내 봅시다.

클라이언트 오브젝트에 [Step 이벤트]를 추가하고, 서버로 메시지를 보냅니다.

 

 
obj_client 오브젝트 - Step 이벤트
 
 
if keyboard_check_pressed( vk_enter ){
 
keyboard_string = string_replace_all( keyboard_string, "\n", "" );
if string_length( keyboard_string )>100{ keyboard_string = string_copy( keyboard_string, 1, 100 ); }
 
if !( keyboard_string == "" ){
// == == ==
buffer_seek( global.net_buffer, buffer_seek_start, 0 );
buffer_write( global.net_buffer, buffer_u16, 5 ); //패킷 이벤트 번호
 
buffer_write( global.net_buffer, buffer_string, "["+global.playername+"]"+keyboard_string );
 
network_send_packet( global.client_socket, global.net_buffer, buffer_tell( global.net_buffer ) );
// == == ==
keyboard_string = "";
}
 
}
 

 

수신 이벤트 번호를 서버와 클라이언트가 다르게 처리할 수 있지만, 코딩할 떄 동일한 번호로 처리하는 것이

수정하거나 확인하기가 좋습니다.

강좌에서는 서버에서도 클라이언트와 동일하게 5번 수신 이벤트에서 처리하겠습니다.

 

 

서버 오브젝트 [네트워크 비동기 이벤트]의 5번 수신 이벤트에서 클라이언트가 보낸 메시지 패킷을 정리합니다.

 

 
obj_server 오브젝트 - Async - Networking 이벤트
 
 
var n_id = ds_map_find_value( async_load, "id" );
 
//--------- 서버 IP 체크
if n_id = global.local_socket{
var _ip = ds_map_find_value( async_load, "ip" );
global.local_server_IP = _ip;
exit;
}
//--------- 서버 IP 체크
 
 
if n_id == global.server_socket{
//--------- 클라이언트 연결/해제 체크
//연결 어쩌고... 해제 저쩌고... 이벤트가 있었으나 길어서 잠시 생략함.
//--------- 클라이언트 연결/해제 체크
}
 
else{
 
var sock = ds_map_find_value( async_load, "id" ); //클라이언트 소켓
var t_buffer = ds_map_find_value( async_load, "buffer" ); //버퍼
 
var cmd_type = buffer_read( t_buffer, buffer_u16 ); //수신 패킷 이벤트 번호
 
var pos = ds_list_find_index( global.socketlist, sock ); //저장한 소켓 목록
 
switch ( cmd_type ){
 
case 1:
// 새로운 클라이언트 접속시 세부 정보 수신
break;
 
//--------- ▼ 클라이언트 데이터 수신
case 5:
 
var t = buffer_read( t_buffer, buffer_string );
ds_list_add( global.chat_list, t );
 
//받은 메시지는 다시 모든 클라이언트에게 보냄
buffer_seek( global.net_buffer, buffer_seek_start, 0 );
buffer_write( global.net_buffer , buffer_u16, 5 ); //패킷 번호
buffer_write( global.net_buffer , buffer_string, t );
 
for ( var i = 0; i < ds_list_size( global.socketlist ); i+ = 1; ){
network_send_packet( ds_list_find_value( global.socketlist, i ), global.net_buffer, buffer_tell( global.net_buffer ) );
}
 
break;
}
//--------- ▲ 클라이언트 데이터 수신
 
}
 
 

 

당연하지만, 클라이언트가 여러명일 경우, 클라이언트 1명이 보낸 메시지는 서버밖에 모릅니다.

따라서 서버는 다른 클라이언트에게도 메시지를 보내주어야 채팅 참여자 모두가 메시지를 확인할 수 있습니다.

 

 

 

 

 

◈ 네트워크 비동기 이벤트 종합 정리

 

여기까지 [네트워크 비동기 이벤트]를 어떻게 구성했는지 정리해봅시다.

먼저 서버 오브젝트의 [네트워크 비동기 이벤트]입니다.

 

 
obj_server 오브젝트 - Async - Networking 이벤트
 
 
var n_id = ds_map_find_value( async_load, "id" );
 
//--------- 서버 IP 체크
if n_id = global.local_socket{
var _ip = ds_map_find_value( async_load, "ip" );
global.local_server_IP = _ip;
exit;
}
//--------- 서버 IP 체크
 
 
 
if n_id == global.server_socket{
//--------- 클라이언트 연결/해제 체크
var type = ds_map_find_value( async_load, "type" );
 
switch( type ){
case network_type_connect://현재 소켓에 새로운 클라이언트 접속
var sock = ds_map_find_value( async_load, "socket" );
ds_list_add( global.socketlist, sock );
 
buffer_seek( global.net_buffer, buffer_seek_start, 0 );
buffer_write( global.net_buffer , buffer_u16, 2 ); //패킷 이벤트 번호
 
buffer_write( global.net_buffer , buffer_u16, sock ); //접속자에게 유저 id 할당
buffer_write( global.net_buffer , buffer_string, "환영합니다!" );
network_send_packet( sock, global.net_buffer, buffer_tell( global.net_buffer ) );
 
global.client_ping[sock] = 0; //ping 체크
//
break;
 
case network_type_disconnect://현재 소켓에 클라이언트 연결 종료
var sock = ds_map_find_value( async_load, "socket" );
 
var t, pos;
pos = ds_list_find_index( global.socketlist, sock );
ds_list_delete( global.socketlist, pos ); //클라이언트 소켓
 
t = "["+string( ds_map_find_value( global.player_map, "player_"+string( sock ) ) )+"]님이 나갔습니다.";
ds_list_add( global.chat_list, t );
 
if ds_map_exists( global.player_map, "player_"+string( sock ) )
{ ds_map_delete( global.player_map, "player_"+string( sock ) ); }
 
//클라이언트들에게 보낼 접속자 정보
var _map = json_encode( global.player_map );
//
buffer_seek( global.net_buffer, buffer_seek_start, 0 );
buffer_write( global.net_buffer , buffer_u16, 1 ); //패킷 이벤트 번호
 
buffer_write( global.net_buffer , buffer_string, global.playername ); //서버 관리자 닉네임
buffer_write( global.net_buffer , buffer_string, t ); //챗
buffer_write( global.net_buffer , buffer_string, _map ); //클라이언트 정보
 
for ( var i = 0; i < ds_list_size( global.socketlist ); i+ = 1; )
{
network_send_packet( ds_list_find_value( global.socketlist, i ), global.net_buffer, buffer_tell( global.net_buffer ) );
}
 
break;
 
 
 
}
//--------- 클라이언트 연결/해제 체크
}
 
else{
// == == == == == == == == == == == 클라이언트 데이터 수신
var sock = ds_map_find_value( async_load, "id" );
var t_buffer = ds_map_find_value( async_load, "buffer" );
 
var cmd_type = buffer_read( t_buffer, buffer_u16 );
var pos = ds_list_find_index( global.socketlist, sock );
 
switch ( cmd_type ){
//--------- 새로운 클라이언트 접속시 세부 정보 수신
case 1:
var _name = buffer_read( t_buffer, buffer_string ); //플레이어 이름
ds_map_set( global.player_map, "player_"+string( sock ), string( _name ) );
 
//서버 챗팅 적용
var t = "["+_name+"]님이 접속하셨습니다.";
ds_list_add( global.chat_list, t );
 
 
//클라이언트들에게 보낼 접속자 정보
if !( ds_map_exists( global.player_map, "player_order" ) ){ ds_map_add_list( global.player_map, "player_order", global.socketlist ); }
else{ ds_map_replace_list( global.player_map, "player_order", global.socketlist ); }
var _map = json_encode( global.player_map );
 
//클라이언트에 패킷을 보내 적용
buffer_seek( global.net_buffer, buffer_seek_start, 0 );
buffer_write( global.net_buffer , buffer_u16, 1 ); //패킷 이벤트 번호
 
buffer_write( global.net_buffer , buffer_string, global.playername ); //서버 관리자 닉네임
buffer_write( global.net_buffer , buffer_string, t ); //챗
 
buffer_write( global.net_buffer , buffer_string, _map ); //클라이언트 정보
 
for ( var i = 0; i < ds_list_size( global.socketlist ); i+ = 1; ){
network_send_packet( ds_list_find_value( global.socketlist, i ), global.net_buffer, buffer_tell( global.net_buffer ) );
}
break;
//--------- 새로운 클라이언트 접속시 세부 정보 수신
 
//--------- 채팅
case 5:
var t = buffer_read( t_buffer, buffer_string );
ds_list_add( global.chat_list, t );
 
//
buffer_seek( global.net_buffer, buffer_seek_start, 0 );
buffer_write( global.net_buffer , buffer_u16, 5 ); //패킷 번호
buffer_write( global.net_buffer , buffer_string, t );
 
for ( var i = 0; i < ds_list_size( global.socketlist ); i+ = 1; ){
network_send_packet( ds_list_find_value( global.socketlist, i ), global.net_buffer, buffer_tell( global.net_buffer ) );
}
 
break;
//--------- 채팅
 
 
 
}
// == == == == == == == == == == == 클라이언트 데이터 수신
}
 

 

다음은 클라이언트 오브젝트의 [네트워크 비동기 이벤트]를 확인해봅시다.

 

 
obj_client 오브젝트 - Async - Networking 이벤트
 
 
var n_id = ds_map_find_value( async_load, "id" );
if n_id == global.client_socket{
 
var type = ds_map_find_value( async_load, "type" );
 
if ( type == network_type_data ){
 
// == == == == == == == == == == == = 서버로 부터 받은 패킷 데이터
 
var t_buffer = ds_map_find_value( async_load, "buffer" );
var cmd_type = buffer_read( t_buffer, buffer_u16 );
switch ( cmd_type ){
//--------- 클라이언트 세부 정보 갱신
case 1:
global.server_name = buffer_read( t_buffer, buffer_string );
var t = buffer_read( t_buffer, buffer_string );
 
ds_map_destroy( global.player_map );
global.player_map = json_decode( buffer_read( t_buffer, buffer_string ) ); //접속자들 정보
 
ds_list_add( global.chat_list, t );
 
break;
//--------- 클라이언트 세부 정보 갱신
 
//--------- 서버에 처음 접속했을 때
case 2:
var _id = buffer_read( t_buffer, buffer_u16 );
var t = buffer_read( t_buffer, buffer_string );
 
global.user_id = _id;
 
ds_list_add( global.chat_list, t );
break;
//--------- 서버에 처음 접속했을 때
 
//--------- 챗팅
case 5:
var t = buffer_read( t_buffer, buffer_string );
ds_list_add( global.chat_list, t );
break;
//--------- 챗팅
}
 
// == == == == == == == == == == == = 서버로 부터 받은 패킷 데이터
 
}
}
 
 

 

이제 서버와 클라이언트간의 대화가 가능해졌습니다.

잘 작동하는지 테스트해봅시다.

 

 

 

 

 

 

 

 

300x250

댓글