diff --git a/api/event.php b/api/event.php new file mode 100644 index 0000000..fc06dc3 --- /dev/null +++ b/api/event.php @@ -0,0 +1,1055 @@ + 25, + 'red' => 9, + 'blue' => 8, + 'mrx' => 1, + 'rows' => 5, + 'cols' => 5, +]; + +$gi = (object) [ + 'total' => $d->total, + 'reds' => $d->red, + 'blues' => $d->blue, + 'mrxs' => $d->mrx, + 'rows' => $d->rows, + 'cols' => $d->cols, + 'red_left' => $d->red, + 'blue_left' => $d->blues, + 'mrx_left' => $d->mrx, + 'other_left' => $d->total - $d->red - $d->blue - $d->mrx, + 'gameover' => false, +]; + +while (true) { + $sockets_change = $sockets; + socket_select($sockets_change, $null, $null, 0, 10); + + if(in_array($socket, $sockets_change)) { + $client = socket_accept($socket); + $sockets[] = $client; + + $header = socket_read($client, 1024); + perform_handshaking($header, $client, $host, $port); + $found_socket = array_search($socket, $sockets_change); + unset($sockets_change[$found_socket]); + if(!isset($selectedWords)) { + $selectedWords = getWords($gi->total, $gi->reds, $gi->blues, $gi->mrxs); + } + $response_text = mask(json_encode([ + 'type' => 'init', + 'data' => [ + 'cols' => $gi->cols, + 'rows' => $gi->rows, + 'words' => $selectedWords + ] + ])); + send_message($response_text, $sockets); //send data + } + + foreach($sockets_change as $s) { + while(socket_recv($s, $buf, 1024, 0) >= 1) + { + $received_text = unmask($buf); //unmask data + $clientMsg = json_decode($received_text); //json decode + $broadMsg = []; + $forceEndTurn = false; + $broadChatMsg = []; + switch($clientMsg->type) { + case 'new_game': + if(!$gi->gameover) break; + send_new_game($socketUsers[$s]); + break; + case 'join': + $socketUsers[$s] = $clientMsg->name; + send_player_joined($clientMsg->name); + break; + case 'chat_send': + send_chat_msg($socketUsers[$s], $clientMsg->msg); + break; + case 'turnend': + if($gi->gameover) break; + send_turnend($clientMsg->team); + break; + case 'cardplayed': + if($gi->gameover) break; + send_cardplayed($clientMsg, $socketUsers[$s]); + break; + } + break 2; //exit this loop + } + + $buf = @socket_read($s, 1024, PHP_NORMAL_READ); + if ($buf === false) { // check disconnected client + // remove client for $clients array + $found_socket = array_search($s, $sockets); + socket_getpeername($s, $ip); + unset($sockets[$found_socket]); + } + } +} + +function send_message($msg, $clients) +{ + foreach($clients as $c) { + @socket_write($c, $msg, strlen($msg)); + } + return true; +} + +function send_message_smpl($msg) +{ + global $sockets; + send_message(mask(json_encode($msg)), $sockets); +} + +function send_player_joined($name) { + send_message_smpl([ + 'type' => 'player_joined', + 'data' => [ + 'name' => $name, + 'time' => get_now() + ] + ]); +} + +function send_turnend($team) { + send_message_smpl([ + 'type' => 'next_player', + 'data' => [ + 'team' => opponent($team) + ] + ]); +} + +function send_cardplayed($msg, $name) { + global $gi; + global $selectedWords; + + $c = $msg->col_index; + $r = $msg->row_index; + $team = $msg->team; + $word = $selectedWords[$r*$gi->cols + $c]; + $wordValue = $word['value']; + + $cardTeam = 'neutral'; + if(red($word)) { + $cardTeam = 'red'; + } else if(blue($word)) { + $cardTeam = 'blue'; + } else if(mrx($word)) { + $cardTeam = 'mr x'; + } + send_message_smpl([ + 'type' => 'reveal', + 'data' => [ + 'row' => $msg->row_index, + 'col' => $msg->col_index, + ] + ]); + + send_chat_msg('', "$name ($team) revealed card $wordValue ($cardTeam)", true); + + if(other_team($team, $word)) { + send_turnend($team); + } + + update_stats($word, $team); +} + +function send_chat_msg($name, $msg, $isSystem = false) { + send_message_smpl([ + 'type' => 'chat_received', + 'data' => [ + 'sender' => $name, + 'isSystem' => $isSystem, + 'msg' => $msg, + 'time' => get_now() + ] + ]); +} + +function send_game_over($winner) { + send_message_smpl([ + 'type' => 'game_over', + 'data' => [ + 'winner' => $winner, + ] + ]); + send_chat_msg('', "Team $winner won the game! Congratulations!", true); +} + +function send_new_game($name) { + global $gi; + global $d; + global $selectedWords; + + reset_stats($d->total, $d->rows, $d->cols, $d->red, $d->blue, $d->mrx); + + $selectedWords = getWords($gi->total, $gi->reds, $gi->blues, $gi->mrxs); + + send_message_smpl([ + 'type' => 'new_game', + 'data' => [ + 'cols' => $gi->cols, + 'rows' => $gi->rows, + 'words' => $selectedWords + ] + ]); + + send_chat_msg('', "New game started by $name. Good luck!", true); +} + +function get_now() { + return time() * 1000; +} + +function other_team($team, $word) { + return (red($word) && !red($team)) || + (blue($word) && !blue($team)) || + (!red($word) && !blue($word)); +} + +function opponent($team) { + return red($team) ? 'blue' : 'red'; +} + +function red($card) { + return is_array($card) ? $card['red'] : $card === 'red'; +} + +function blue($card) { + return is_array($card) ? $card['blue'] : $card === 'blue'; +} + +function mrx($card) { + return $card['mr_x']; +} + +function neutral($card) { + return !red($card) && !blue($card) && !mrx($card); +} + +function is_card_revealed($card) { + return $card['revealed']; +} + +function reset_stats($total = 25, $rows = 5, $cols = 5, $reds = 9, $blues = 8, $mrx = 1) { + global $gi; + + $gi->total = $total; + $gi->reds = $reds; + $gi->blues = $blues; + $gi->mrxs = $mrx; + $gi->rows = $rows; + $gi->cols = $cols; + $gi->red_left = $reds; + $gi->blue_left = $blues; + $gi->mrx_left = $mrx; + $gi->other_left = $total - $reds - $blues - $mrx; + $gi->gameover = false; +} + +function update_stats($card, $team) { + global $gi; + + $winner = ''; + + if(red($card)) { + $gi->red_left--; + if($gi->red_left === 0) { + $winner = 'red'; + } + } else if(blue($card)) { + $gi->blue_left--; + if($gi->blue_left === 0) { + $winner = 'blue'; + } + } else if(mrx($card)) { + $gi->mrx_left--; + if($gi->mrx_left === 0) { + $winner = opponent($team); + } + } else { + $gi->other_left--; + } + + if($winner !== '' && (red($winner) || blue($winner))) { + send_game_over($winner); + $gi->gameover = true; + } +} + +//Unmask incoming framed message +function unmask($text) { + $length = ord($text[1]) & 127; + if($length == 126) { + $masks = substr($text, 4, 4); + $data = substr($text, 8); + } + elseif($length == 127) { + $masks = substr($text, 10, 4); + $data = substr($text, 14); + } + else { + $masks = substr($text, 2, 4); + $data = substr($text, 6); + } + $text = ""; + for ($i = 0; $i < strlen($data); ++$i) { + $text .= $data[$i] ^ $masks[$i%4]; + } + return $text; +} + +//Encode message for transfer to client. +function mask($text) +{ + $b1 = 0x80 | (0x1 & 0x0f); + $length = strlen($text); + + if($length <= 125) + $header = pack('CC', $b1, $length); + elseif($length > 125 && $length < 65536) + $header = pack('CCn', $b1, 126, $length); + elseif($length >= 65536) + $header = pack('CCNN', $b1, 127, $length); + return $header.$text; +} + +//handshake new client. +function perform_handshaking($recieved_header,$client_conn, $host, $port) +{ + $headers = array(); + $lines = preg_split("/\r\n/", $recieved_header); + foreach($lines as $line) + { + $line = chop($line); + if(preg_match('/\A(\S+): (.*)\z/', $line, $matches)) + { + $headers[$matches[1]] = $matches[2]; + } + } + + $secKey = $headers['Sec-WebSocket-Key']; + $secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))); + //hand shaking header + $upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" . + "Upgrade: websocket\r\n" . + "Connection: Upgrade\r\n" . + "WebSocket-Origin: $host\r\n" . + "WebSocket-Location: ws://$host:$port/demo/shout.php\r\n". + "Sec-WebSocket-Accept:$secAccept\r\n\r\n"; + socket_write($client_conn,$upgrade,strlen($upgrade)); +} + +function getWords($count = 25, $reds = 9, $blues = 8, $mr_x = 1) { + $selection = []; + global $words; + $localWords = $words; + shuffle($localWords); + for($i=0; $i<$count; $i++) { + $next = array_pop($localWords); + $val = ''; + if($reds > 0) { + $reds--; + $val = 'red'; + } else if($blues > 0) { + $blues--; + $val = 'blue'; + } else if($mr_x > 0) { + $mr_x--; + $val = 'mrx'; + } + $selection[] = [ + 'value' => $next, + 'red' => $val === 'red', + 'blue' => $val === 'blue', + 'mr_x' => $val === 'mrx', + 'revealed' => false + ]; + } + + shuffle($selection); + return $selection; +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..437057f --- /dev/null +++ b/index.html @@ -0,0 +1,352 @@ + + + + + + + + + Codenames + + + + + + + + +
+
+
+
+
+ + {{ openRedCards }} + + - + + {{ openBlueCards }} + +
+
+

+ {{ winner }} won! +

+ + {{ teamName }}'s turn + +
+
+
+
+
+ {{ words[i*size.cols + j].value }} +
+
+
+
+
+ + + +
+
+
+ Connected as {{ username }} +
+
+ + +
+
+
+ + + System Message - {{ formatDate(msg.time) }} + + + {{ msg.sender }} - {{ formatDate(msg.time) }} + + + {{ formatDate(msg.time) }} - You + + +
+ {{ msg.msg }} +
+
+
+
+
+
+ +
+ +
+
+
+
+
+
+
+
+ + +
+ + +
+
+
+ + + +