#include "ipc_state.h" #define _WEBSOCKETPP_CPP11_STRICT_ #include #include #include #include #include #include #include #include #include #include #include #include "ap_state.h" #include "logger.h" #include "tracker_frame.h" namespace { struct IPCState { std::mutex state_mutex; TrackerFrame* tracker_frame = nullptr; // Protected state bool initialized = false; std::string address; bool should_disconnect = false; std::optional status_message; bool slot_matches = false; std::string tracker_ap_server; std::string tracker_ap_user; std::string game_ap_server; std::string game_ap_user; std::optional> player_position; std::set solved_panels; // Thread state std::unique_ptr ws; bool connected = false; void SetTrackerFrame(TrackerFrame* frame) { tracker_frame = frame; } void Connect(std::string a) { // This is the main concurrency concern, as it mutates protected state in an // important way. Thread() is documented with how it interacts with this // function. std::lock_guard state_guard(state_mutex); if (!initialized) { std::thread([this]() { Thread(); }).detach(); initialized = true; } else if (address != a) { should_disconnect = true; } address = a; } std::optional GetStatusMessage() { std::lock_guard state_guard(state_mutex); return status_message; } void SetTrackerSlot(std::string server, std::string user) { // This function is called from the APState thread, not the main thread, and // it mutates protected state. It only really competes with OnMessage(), when // a "Connect" message is received. If this is called right before, and the // tracker slot does not match the old game slot, it will initiate a // disconnect, and then the OnMessage() handler will see should_disconnect // and stop processing the "Connect" message. If this is called right after // and the slot does not match, IPC will disconnect, which is tolerable. std::lock_guard state_guard(state_mutex); tracker_ap_server = std::move(server); tracker_ap_user = std::move(user); CheckIfSlotMatches(); if (!slot_matches) { should_disconnect = true; address.clear(); } } bool IsConnected() { std::lock_guard state_guard(state_mutex); return slot_matches; } std::optional> GetPlayerPosition() { std::lock_guard state_guard(state_mutex); return player_position; } std::set GetSolvedPanels() { std::lock_guard state_guard(state_mutex); return solved_panels; } private: void Thread() { for (;;) { // initialized is definitely true because it is set to true when the thread // is created and only set to false within this block, when the thread is // killed. Thus, a call to Connect would always at most set // should_disconnect and address. If this happens before this block, it is // as if we are starting from a new thread anyway because should_disconnect // is immediately reset. If a call to Connect happens after this block, // then a connection attempt will be made to the wrong address, but the // thread will grab the mutex right after this and back out the wrong // connection. std::string ipc_address; { std::lock_guard state_guard(state_mutex); SetStatusMessage("Disconnected from game."); should_disconnect = false; slot_matches = false; game_ap_server.clear(); game_ap_user.clear(); player_position = std::nullopt; solved_panels.clear(); if (address.empty()) { initialized = false; return; } ipc_address = address; SetStatusMessage("Connecting to game..."); } int backoff_amount = 0; TrackerLog(fmt::format("Looking for game over IPC ({})...", ipc_address)); while (!connected) { if (TryConnect(ipc_address)) { int backoff_limit = (backoff_amount + 1) * 10; for (int i = 0; i < backoff_limit && !connected; i++) { // If Connect is called right before this block, we will see and // handle should_disconnect. If it is called right after, we will do // one bad poll, one sleep, and then grab the mutex again right // after. { std::lock_guard state_guard(state_mutex); if (should_disconnect) { break; } } ws->poll(); // Back off std::this_thread::sleep_for(std::chrono::milliseconds(100)); } backoff_amount++; } else { std::lock_guard state_guard(state_mutex); if (!should_disconnect) { should_disconnect = true; address.clear(); SetStatusMessage("Disconnected from game."); } break; } // If Connect is called right before this block, we will see and handle // should_disconnect. If it is called right after, and the connection // was unsuccessful, we will grab the mutex after one bad connection // attempt. If the connection was successful, we grab the mutex right // after exiting the loop. bool show_error = false; { std::lock_guard state_guard(state_mutex); if (should_disconnect) { break; } else if (!connected) { if (backoff_amount >= 10) { should_disconnect = true; address.clear(); SetStatusMessage("Disconnected from game."); show_error = true; } else { TrackerLog(fmt::format("Retrying IPC in {} second(s)...", backoff_amount + 1)); } } } // We do this after giving up the mutex because otherwise we could // deadlock with the main thread. if (show_error) { TrackerLog("Giving up on IPC."); wxMessageBox("Connection to Lingo timed out.", "Connection failed", wxOK | wxICON_ERROR); break; } } // Pretty much every lock guard in the thread is the same. We check for // should_disconnect, and if it gets set directly after the block, we do // minimal bad work before checking for it again. { std::lock_guard state_guard(state_mutex); if (should_disconnect) {
<?php
// +----------------------------------------------------------------------+
// | PHP Version 4                                                        |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2004 The PHP Group                                |
// +----------------------------------------------------------------------+
// | This source file is subject to version 3.0 of the PHP license,       |
// | that is bundled with this package in the file LICENSE, and is        |
// | available at through the world-wide-web at                           |
// | http://www.php.net/license/3_0.txt.                                  |
// | If you did not receive a copy of the PHP license and are unable to   |
// | obtain it through the world-wide-web, please send a note to          |
// | license@php.net so we can mail you a copy immediately.               |
// +----------------------------------------------------------------------+
// | Authors: Aidan Lister <aidan@php.net>                                |
// +----------------------------------------------------------------------+
//
// $Id: var_export.php,v 1.2 2005/11/21 10:57:23 ggiunta Exp $


/**
 * Replace var_export()
 *
 * @category    PHP
 * @package     PHP_Compat
 * @link        http://php.net/function.var_export
 * @author      Aidan Lister <aidan@php.net>
 * @version     $Revision: 1.2 $
 * @since       PHP 4.2.0
 * @require     PHP 4.0.0 (user_error)
 */
if (!function_exists('var_export')) {
    function var_export($array, $return = false, $lvl=0)
    {
        // Common output variables
        $indent      = '  ';
        $doublearrow = ' => ';
        $lineend     = ",\n";
        $stringdelim = '\'';

        // Check the export isn't a simple string / int
        if (is_string($array)) {
            $out = $stringdelim . str_replace('\'', '\\\'', str_replace('\\', '\\\\', $array)) . $stringdelim;
        } elseif (is_int($array) || is_float($array)) {
            $out = (string)$array;
        } elseif (is_bool($array)) {
            $out = $array ? 'true' : 'false';
        } elseif (is_null($array)) {
            $out = 'NULL';
        } elseif (is_resource($array)) {
            $out = 'resource';
        } else {
            // Begin the array export
            // Start the string
            $out = "array (\n";

            // Loop through each value in array
            foreach ($array as $key => $value) {
                // If the key is a string, delimit it
                if (is_string($key)) {
                    $key = str_replace('\'', '\\\'', str_replace('\\', '\\\\', $key));
                    $key = $stringdelim . $key . $stringdelim;
                }

                $val = var_export($value, true, $lvl+1);
                // Delimit value
                /*if (is_array($value)) {
                    // We have an array, so do some recursion
                    // Do some basic recursion while increasing the indent
                    $recur_array = explode($newline, var_export($value, true));
                    $temp_array = array();
                    foreach ($recur_array as $recur_line) {
                        $temp_array[] = $indent . $recur_line;
                    }
                    $recur_array = implode($newline, $temp_array);
                    $value = $newline . $recur_array;
                } elseif (is_null($value)) {
                    $value = 'NULL';
                } else {
                    $value = str_replace($find, $replace, $value);
                    $value = $stringdelim . $value . $stringdelim;
                }*/

                // Piece together the line
                for ($i = 0; $i < $lvl; $i++)
                    $out .= $indent;
                $out .= $key . $doublearrow . $val . $lineend;
            }

            // End our string
            for ($i = 0; $i < $lvl; $i++)
                $out .= $indent;
            $out .= ")";
        }

        // Decide method of output
        if ($return === true) {
            return $out;
        } else {
            echo $out;
            return;
        }
    }
}
?>