Newer
Older
codenames / api / event.php
@Vinzenz Rosenkranz Vinzenz Rosenkranz on 9 May 2020 19 KB dockerize codenames
<?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 = '0.0.0.0';
$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, $clientMsg->options);
                    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, $options = null, $isSystem = false) {
    send_message_smpl([
        'type' => 'chat_received',
        'data' => [
            'sender' => $name,
            'team' => isset($options->team) ? $options->team : null,
            '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;
}