<?php
error_reporting(E_ERROR);
set_time_limit (0);
header('Content-Type: text/html; charset=utf-8');
$null = null;
$words = [
"Ärger",
"Ärztin",
"Abend",
"Abfahrt",
"Abflug",
"Absender",
"Adresse",
"Alkohol",
"Alter",
"Ampel",
"Anfang",
"Angebot",
"Angestellte",
"Angst",
"Ankunft",
"Anmeldung",
"Anrede",
"Anruf",
"Anrufbeantworter",
"Ansage",
"Anschluss",
"Antwort",
"Anzeige",
"Anzug",
"Apfel",
"Apotheke",
"Appartement",
"Appetit",
"April",
"Arbeit",
"Arbeitsplatz",
"Arm",
"Arzt",
"Aufenthalt",
"Aufgabe",
"Aufzug",
"Auge",
"August",
"Ausbildung",
"Ausflug",
"Ausgang",
"Auskunft",
"Ausländer",
"Ausländerin",
"Ausland",
"Aussage",
"Ausstellung",
"Ausweis",
"Auto",
"Autobahn",
"Automat",
"Bäckerei",
"Büro",
"Baby",
"Bad",
"Bahn",
"Bahnhof",
"Bahnsteig",
"Balkon",
"Banane",
"Bank",
"Batterie",
"Baum",
"Beamte",
"Beamtin",
"Bein",
"Beispiel",
"Bekannte",
"Benzin",
"Beratung",
"Berg",
"Beruf",
"Berufsschule",
"Besuch",
"Betrag",
"Bett",
"Bewerbung",
"Bier",
"Bild",
"Bildschirm",
"Birne",
"Bitte",
"Blatt",
"Bleistift",
"Blick",
"Blume",
"Bluse",
"Blut",
"Bogen",
"Bohne",
"Brötchen",
"Brücke",
"Brief",
"Briefkasten",
"Briefmarke",
"Brieftasche",
"Briefumschlag",
"Brille",
"Brot",
"Bruder",
"Buch",
"Buchstabe",
"Bus",
"Butter",
"Café",
"CD",
"CD-ROM",
"Chef",
"Computer",
"Creme",
"Dach",
"Dame",
"Dank",
"Datum",
"Dauer",
"Deutsche",
"Dezember",
"Dienstag",
"Ding",
"Disco",
"Doktor",
"Dom",
"Donnerstag",
"Doppelzimmer",
"Dorf",
"Drucker",
"Durchsage",
"Durst",
"Dusche",
"E-Mail",
"Ecke",
"Ehefrau",
"Ehemann",
"Ei",
"Einführung",
"Eingang",
"Einladung",
"Eintritt",
"Einwohner",
"Einzelzimmer",
"Eis",
"Eltern",
"Empfänger",
"Empfang",
"Ende",
"Enkel",
"Entschuldigung",
"Erdgeschoss",
"Erfahrung",
"Ergebnis",
"Erlaubnis",
"Ermäßigung",
"Erwachsene",
"Essen",
"Export",
"Fähre",
"Führerschein",
"Führung",
"Fabrik",
"Fahrer",
"Fahrkarte",
"Fahrplan",
"Fahrrad",
"Familie",
"Familienname",
"Familienstand",
"Farbe",
"Fax",
"Februar",
"Fehler",
"Fenster",
"Ferien",
"Fernsehgerät",
"Fest",
"Feuer",
"Feuerwehr",
"Feuerzeug",
"Fieber",
"Film",
"Firma",
"Fisch",
"Flasche",
"Fleisch",
"Flughafen",
"Flugzeug",
"Flur",
"Fluss",
"Formular",
"Foto",
"Fotoapparat",
"Frühjahr",
"Frühling",
"Frühstück",
"Frage",
"Frau",
"Freitag",
"Freizeit",
"Freund",
"Freundin",
"Friseur",
"Frist",
"Fuß",
"Fußball",
"Fundbüro",
"Gabel",
"Garage",
"Garten",
"Gas",
"Gast",
"Gebühr",
"Geburtsjahr",
"Geburtsort",
"Geburtstag",
"Gegenteil",
"Geld",
"Geldbörse",
"Gemüse",
"Gepäck",
"Gericht",
"Gesamtschule",
"Geschäft",
"Geschenk",
"Geschirr",
"Geschwister",
"Gesicht",
"Gespräch",
"Gesundheit",
"Getränk",
"Gewicht",
"Gewitter",
"Glück",
"Glückwunsch",
"Glas",
"Gleis",
"Goethe-Institut",
"Größe",
"Die Grenze",
"Grippe",
"Großeltern",
"Großmutter",
"Großvater",
"Gruß",
"Grundschule",
"Gruppe",
"Guthaben",
"Gymnasium",
"Hähnchen",
"Haar",
"Halbpension",
"Halle",
"Hals",
"Haltestelle",
"Hand",
"Handtuch",
"Handy",
"Haus",
"Hausaufgabe",
"Hausfrau",
"Haushalt",
"Hausmann",
"Heimat",
"Heizung",
"Hemd",
"Herbst",
"Herd",
"Herr",
"Herz",
"Hilfe",
"Hobby",
"Holz",
"Hose",
"Hund",
"Hunger",
"Idee",
"Import",
"Industrie",
"Information",
"Inhalt",
"Internet",
"Jacke",
"Jahr",
"Januar",
"Job",
"Jugendherberge",
"Jugendliche",
"Juli",
"Junge",
"Juni",
"Käse",
"Körper",
"Küche",
"Kühlschrank",
"Kündigung",
"Kaffee",
"Kalender",
"Kamera",
"Kanne",
"Karte",
"Kartoffel",
"Kasse",
"Kassette",
"Kassettenrecorder",
"Katze",
"Keller",
"Kellner",
"Kenntnisse",
"Kennzeichen",
"Kette",
"Kfz",
"Kind",
"Kindergarten",
"Kinderwagen",
"Kino",
"Kiosk",
"Kirche",
"Klasse",
"Kleid",
"Kleidung",
"Kneipe",
"Koffer",
"Kollege",
"Kollegin",
"Konsulat",
"Kontakt",
"Konto",
"Kontrolle",
"Konzert",
"Kopf",
"Kosmetik",
"Krankenkasse",
"Krankheit",
"Kredit",
"Kreditkarte",
"Kreis",
"Kreuzung",
"Kuchen",
"Kugelschreiber",
"Kunde",
"Kundin",
"Kurs",
"Löffel",
"Lösung",
"Laden",
"Lager",
"Lampe",
"Land",
"Landschaft",
"Leben",
"Lebensmittel",
"Leid",
"Lehre",
"Lehrer",
"Lehrerin",
"Leute",
"Licht",
"Lied",
"Lkw",
"Loch",
"Lohn",
"Lokal",
"Luft",
"Lust",
"Mädchen",
"März",
"Möbel",
"Müll",
"Mülltonne",
"Magen",
"Mai",
"Mal",
"Mann",
"Mantel",
"Markt",
"Maschine",
"Material",
"Mechaniker",
"Medikament",
"Meer",
"Mehrwertsteuer",
"Meinung",
"Menge",
"Mensch",
"Messer",
"Metall",
"Miete",
"Milch",
"Minute",
"Mittag",
"Mitte",
"Mitteilung",
"Mittel",
"Mittelschule",
"Mittwoch",
"Mode",
"Moment",
"Monat",
"Montag",
"Morgen",
"Motor",
"Mund",
"Museum",
"Musik",
"Mutter",
"Nähe",
"Nachbar",
"Nachbarin",
"Nachmittag",
"Nachrichten",
"Nacht",
"Name",
"Natur",
"Nebel",
"Norden",
"Notarzt",
"Note",
"Notfall",
"Notiz",
"November",
"Nudel",
"Nummer",
"Ober",
"Obst",
"Oktober",
"Oma",
"Opa",
"Operation",
"Orange",
"Ordnung",
"Ort",
"Osten",
"Öl",
"Päckchen",
"Paket",
"Panne",
"Papier",
"Papiere",
"Parfüm",
"Park",
"Partei",
"Partner",
"Partnerin",
"Party",
"Pass",
"Pause",
"Pension",
"Pkw",
"Plan",
"Plastik",
"Platz",
"Polizei",
"Pommes frites",
"Portion",
"Post",
"Postleitzahl",
"Prüfung",
"Praktikum",
"Praxis",
"Preis",
"Problem",
"Das Produkt",
"Programm",
"Prospekt",
"Pullover",
"Qualität",
"Quittung",
"Rücken",
"Rabatt",
"Radio",
"Rathaus",
"Raucher",
"Raucherin",
"Raum",
"Realschule",
"Rechnung",
"Regen",
"Reifen",
"Reinigung",
"Reis",
"Reise",
"Reisebüro",
"Reiseführer",
"Reparatur",
"Restaurant",
"Rezept",
"Rezeption",
"Rind",
"Rock",
"Rose",
"Rundgang",
"Süden",
"S-Bahn",
"Sache",
"Saft",
"Salat",
"Salz",
"Samstag/Sonnabend",
"Satz",
"Schüler",
"Schülerin",
"Schalter",
"Scheckkarte",
"Schiff",
"Schild",
"Schinken",
"Schirm",
"Schlüssel",
"Schloss",
"Schluss",
"Schmerzen",
"Schnee",
"Schnupfen",
"Schokolade",
"Schrank",
"Schuh",
"Schule",
"Schwein",
"Schwester",
"Schwimmbad",
"See",
"Sehenswürdigkeit",
"Seife",
"Sekretärin",
"Sekunde",
"Sendung",
"Senioren",
"September",
"Service",
"Sessel",
"Sofa",
"Sohn",
"Sommer",
"Sonderangebot",
"Sonne",
"Sonntag",
"Sorge",
"Spülmaschine",
"Spaß",
"Spaziergang",
"Speisekarte",
"Spielplatz",
"Sprache",
"Sprachschule",
"Sprechstunde",
"Stück",
"Stadt",
"Standesamt",
"Stempel",
"Steuer",
"Stock",
"Stoff",
"Straße",
"Straßenbahn",
"Strand",
"Streichholz",
"Strom",
"Student",
"Studentin",
"Studium",
"Stuhl",
"Stunde",
"Supermarkt",
"Suppe",
"Tür",
"Tüte",
"Tag",
"Tankstelle",
"Tasche",
"Tasse",
"Taxi",
"Der Tee",
"Teil",
"Telefon",
"Telefonbuch",
"Teller",
"Teppich",
"Termin",
"Test",
"Text",
"Theater",
"Thema",
"Ticket",
"Tier",
"Tipp",
"Tisch",
"Tochter",
"Toilette",
"Tomate",
"Topf",
"Tourist",
"Treppe",
"Trinkgeld",
"Turm",
"U-Bahn",
"Uhr",
"Unfall",
"Universität",
"Unterhaltung",
"Unterkunft",
"Unterricht",
"Unterschied",
"Unterschrift",
"Untersuchung",
"Urlaub",
"Übernachtung",
"Vater",
"Verbindung",
"Verein",
"Verkäufer",
"Verkäuferin",
"Verkehr",
"Vermieter",
"Versicherung",
"Verspätung",
"Vertrag",
"Video",
"Vogel",
"Volksschule",
"Vormittag",
"Vorname",
"Vorsicht",
"Vorwahl",
"Wäsche",
"Wagen",
"Wald",
"Wasser",
"Weg",
"Wein",
"Welt",
"Werkstatt",
"Werkzeug",
"Westen",
"Wetter",
"Wiederhören",
"Wiedersehen",
"Wind",
"Winter",
"Wirtschaft",
"Woche",
"Wochenende",
"Wochentag",
"Wohnung",
"Wolke",
"Wort",
"Wunsch",
"Wurst",
"Zahl",
"Zahn",
"Zeit",
"Zeitschrift",
"Zeitung",
"Zentrum",
"Zettel",
"Zeugnis",
"Zigarette",
"Zimmer",
"Zitrone",
"Zoll",
"Zucker",
"Zug"
];
$selectedWords = null;
$host = 'localhost';
$port = 8000;
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($socket, $host, $port);
socket_listen($socket);
$sockets = [$socket];
$socketUsers = [];
$d = (object) [
'total' => 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;
}