├── .gitignore
├── README.md
├── docs
├── css
│ └── media-queries.md
└── javascript
│ ├── navigator.md
│ └── nx-api.md
└── tools
├── debug-client
├── .gitignore
├── common
│ └── js
│ │ └── socket.js
├── config-example.php
├── debugger
│ ├── index.php
│ └── js
│ │ └── devtool.js
└── switch
│ ├── index.php
│ └── js
│ └── devtool.js
└── debug-server
├── .gitignore
├── DebugServer.php
├── SwitchBrowserDebugDaemonHandler.php
├── composer.json
├── composer.lock
├── config-example.php
└── start.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | */error_log
2 | error_log
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # switch-browser
2 |
3 | Documentation for the Nintendo Switch's inbuilt browser applet, and tools for remote testing and debugging.
4 |
5 | Maintained by [sudofox](https://github.com/sudofox) and [jaames](https://github.com/jaames)
6 |
--------------------------------------------------------------------------------
/docs/css/media-queries.md:
--------------------------------------------------------------------------------
1 | # Media Queries
2 |
3 | ## Custom
4 |
5 | The Nintendo Switch browser has two custom CSS media queries which allow developers to specify styles depending on the device mode.
6 |
7 | ### device-mode
8 |
9 | The `-webkit-nintendo-switch-device-mode` query can be used to detect whether or not the Switch is in console or handheld mode.
10 |
11 | **example:**
12 |
13 | ```css
14 | @media(-webkit-nintendo-switch-device-mode: -webkit-nintendo-switch-device-console) {
15 | /* css styles for console mode */
16 | }
17 |
18 | @media(-webkit-nintendo-switch-device-mode: -webkit-nintendo-switch-device-handheld) {
19 | /* css styles for handheld mode */
20 | }
21 | ```
22 |
23 | ### performance-mode
24 |
25 |
26 | The `-webkit-nintendo-switch-performance-mode` query can be used to detect which performance setting the Switch is currently using.
27 |
28 | **example:**
29 |
30 | ```css
31 | @media(-webkit-nintendo-switch-performance-mode: -webkit-nintendo-switch-performance-0) {
32 | /* css styles for performance setting 0 */
33 | }
34 |
35 | @media(-webkit-nintendo-switch-performance-mode: -webkit-nintendo-switch-performance-1) {
36 | /* css styles for performance setting 1 */
37 | }
38 | ```
39 |
40 | ### sources
41 |
42 | CSS media query definitions can be found in `NintendoSwitch_OpenSources1.0.0/webkit/WebCore/css/CSSValueKeywords.in`
43 |
44 | line 977:
45 |
46 | ```
47 | #if defined(ENABLE_WKC_DEVICE_MODE_CSS_MEDIA) && ENABLE_WKC_DEVICE_MODE_CSS_MEDIA
48 | // (-webkit-nintendo-switch-device-mode:) media feature:
49 | -webkit-nintendo-switch-device-console
50 | -webkit-nintendo-switch-device-handheld
51 | #endif
52 | ```
53 |
54 | line 983:
55 |
56 | ```
57 | #if defined(ENABLE_WKC_PERFORMANCE_MODE_CSS_MEDIA) && ENABLE_WKC_PERFORMANCE_MODE_CSS_MEDIA
58 | // (-webkit-nintendo-switch-performance-mode:) media feature:
59 | -webkit-nintendo-switch-performance-0
60 | -webkit-nintendo-switch-performance-1
61 | #endif
62 | ```
63 |
--------------------------------------------------------------------------------
/docs/javascript/navigator.md:
--------------------------------------------------------------------------------
1 | # Navigator
2 |
3 | | Property | Value |
4 | |:---------|:------|
5 | | `appCodeName` | `Mozilla` |
6 | | `appName` | `Netscape` |
7 | | `appVersion` | See below |
8 | | `platform ` | `Nintendo Switch` |
9 | | `auserAgent` | See below |
10 |
11 | ## appVersion
12 |
13 | | Firmware | Value |
14 | |:---------|:------|
15 | | 2.0.0 | `5.0 (Nintendo Switch; LoginApplet) AppleWebKit/601.6 (KHTML, like Gecko) NF/4.0.0.5.9 NintendoBrowser/5.1.0.13341` |
16 |
17 | ## userAgent
18 |
19 | | Firmware | Value |
20 | |:---------|:------|
21 | | 2.0.0 | `Mozilla/5.0 (Nintendo Switch; LoginApplet) AppleWebKit/601.6 (KHTML, like Gecko) NF/4.0.0.5.9 NintendoBrowser/5.1.0.13341` |
22 |
--------------------------------------------------------------------------------
/docs/javascript/nx-api.md:
--------------------------------------------------------------------------------
1 | # NX api
2 |
3 | `window.nx` exposes a small API for accessing various browser features from within Javascript. The properties and methods documented here are available across all of the Switch's browser applets, however certain applets (such as eshop) may expose more.
4 |
5 | ### Properties
6 |
7 | #### `isKeyboardShown`
8 |
9 | * **Type:** `{Boolean}`
10 |
11 | * **Usage:**
12 |
13 | Indicates whether or not the keyboard UI is active.
14 |
15 | ### Methods
16 |
17 | #### `canHistoryBack`
18 |
19 | * **Returns:** `{Boolean}`
20 |
21 | * **Usage:**
22 |
23 | Returns `true` if the user can navigate to the previous page (untested but assumed).
24 |
25 | #### `endApplet`
26 |
27 | * **Returns:** `{Boolean}`
28 |
29 | * **Usage:**
30 |
31 | Closes the browser applet.
32 |
33 | #### `openTimeoutDialog`
34 |
35 | * **Arguments:**
36 |
37 | * `{String}` message
38 |
39 | * **Usage:**
40 |
41 | Show a message for a short period of time.
42 |
43 | #### `open1ButtonDialog`
44 |
45 | * **Arguments:**
46 |
47 | * `{String}` message
48 | * `{String}` buttonText
49 |
50 | * **Usage:**
51 |
52 | Show a message with a custom 'OK' button.
53 |
54 | * **Example:**
55 |
56 | ```js
57 | nx.open1ButtonDialog("I'm a message!", "OK!");
58 | ```
59 |
60 | #### `open2ButtonDialog`
61 |
62 | * **Arguments:**
63 |
64 | * `{String}` message
65 | * `{String}` left button text
66 | * `{String}` right button text
67 |
68 | * **Usage:**
69 |
70 | Show a message with custom 'Cancel' and 'OK' buttons.
71 |
72 | * **Example:**
73 |
74 | ```js
75 | nx.open2ButtonDialog("I'm a message!", "Cancel", "OK!");
76 | ```
77 |
78 | #### `footer.setDefaultAssign`
79 |
80 | * **Arguments:**
81 |
82 | * `{String}` key (`"A"`, `"B"`, `"X"` or `"Y"`)
83 | * `{String}` label
84 |
85 | * **Usage:**
86 |
87 | Add a prompt for a given button to the footer. The button will retain its default behaviour, but the label given to it can be set to whatever you like.
88 |
89 | #### `footer.setAssign`
90 |
91 | * **Arguments:**
92 |
93 | * `{String}` key (`"A"`, `"B"`, `"X"` or `"Y"`)
94 | * `{String}` label
95 | * `{Function}` callback
96 | * `{Object}` params
97 | * `{String}` `se` (sound effect label)
98 |
99 | * **Usage:**
100 |
101 | Add a prompt for a given button to the footer, and override its default behaviour(?). The `callback` function given will be called whenever the button is pressed.
102 |
103 | * **Example:**
104 |
105 | ```js
106 | // When the A button is pressed, alert the user, and play the "SeWebBtnDecide" sound effect
107 |
108 | nx.footer.setAssign("A", "Test", function() {
109 | alert("A key pressed!")
110 | }, {
111 | se: "SeWebBtnDecide"
112 | });
113 | ```
114 |
115 | #### `footer.unsetAssign`
116 |
117 | * **Arguments:**
118 |
119 | * `{String}` key (`"A"`, `"B"`, `"X"` or `"Y"`)
120 |
121 | * **Usage:**
122 |
123 | Remove a button prompt added with `footer.setAssign`.
124 |
125 | #### `playSystemSe`
126 |
127 | * **Arguments:**
128 |
129 | * `{String}` soundLabel
130 |
131 | * **Usage:**
132 |
133 | Play a predefined sound effect from the following list of `soundLabel` values:
134 |
135 | ```
136 | SeToggleBtnFocus
137 | SeToggleBtnOn
138 | SeToggleBtnOff
139 | SeCheckboxFocus
140 | SeCheckboxOn
141 | SeCheckboxOff
142 | SeRadioBtnFocus
143 | SeRadioBtnOn
144 | SeSelectCheck
145 | SeSelectUncheck
146 | SeBtnDecide
147 | SeTouchUnfocus
148 | SeBtnFocus
149 | SeKeyError
150 | SeDialogOpen
151 | SeWebZoomOut
152 | SeWebZoomIn
153 | SeWebNaviFocus
154 | SeWebPointerFocus
155 | SeFooterFocus
156 | SeFooterDecideBack
157 | SeFooterDecideFinish
158 | SeWebChangeCursorPointer
159 | SeWebTouchFocus
160 | SeWebLinkDecide
161 | SeWebTextboxStartEdit
162 | SeWebButtonDecide
163 | SeWebRadioBtnOn
164 | SeWebCheckboxUncheck
165 | SeWebCheckboxCheck
166 | SeWebMenuListOpen
167 | ```
168 |
169 | #### `stopSystemSe`
170 |
171 | * **Usage:**
172 |
173 | Stop sound effect playback.
--------------------------------------------------------------------------------
/tools/debug-client/.gitignore:
--------------------------------------------------------------------------------
1 | config.php
2 |
--------------------------------------------------------------------------------
/tools/debug-client/common/js/socket.js:
--------------------------------------------------------------------------------
1 | var socket = (function () {
2 | var connection;
3 | var messageCallbacks = [];
4 | var config = {};
5 |
6 | function sendMessage(type, content) {
7 | var message = {
8 | type: type,
9 | contentType: Array.isArray(content) ? "array" : typeof content,
10 | };
11 | // convert the message content to a string
12 | switch (message.contentType) {
13 | case "object":
14 | case "array":
15 | // objects and arrays can contain self references, which choke JSON.stringify
16 | // so we build up a cashe of referenced objects, and disallow multiple instances
17 | var cache = [];
18 | message.content = JSON.stringify(content, function(key, value) {
19 | // if the value is a function, we want to serialize it
20 | if (typeof value === "function") {
21 | return value.toString();
22 | } else {
23 | // if the value is an object, there's a chance it may reference itself, so we need to check that
24 | if (typeof value === "object" && value !== null) {
25 | // if the value has already been read, ignore it
26 | if (cache.indexOf(value) !== -1) return;
27 | // else push it to the cache so we don't read it again
28 | cache.push(value);
29 | }
30 | return value;
31 | }
32 | });
33 | cache = null;
34 | break;
35 | default:
36 | message.content = content + "";
37 | break;
38 | }
39 | // send the message as a string
40 | if (connection) connection.send(JSON.stringify(message));
41 | };
42 |
43 | function receiveMessage(message) {
44 | var messageData = message.data;
45 | // convert the message string back into an object
46 | var message = JSON.parse(messageData);
47 | // convert message content back into whatever type it was
48 | var content;
49 | switch (message.contentType) {
50 | case "object":
51 | case "array":
52 | content = JSON.parse(message.content);
53 | break;
54 | case "string":
55 | content = message.content;
56 | break;
57 | default:
58 | content = eval(message.content);
59 | break;
60 | }
61 |
62 | message.content = content;
63 |
64 | messageCallbacks.forEach(function (callback) {
65 | callback(message);
66 | });
67 | };
68 |
69 | return {
70 | start: function(opts) {
71 | connection = new WebSocket(opts.host + ":" + opts.port);
72 |
73 | config = opts;
74 |
75 | connection.addEventListener("open", function (event) {
76 | connection.send("REGISTER_" + opts.mode.toUpperCase());
77 | });
78 |
79 | connection.addEventListener("message", receiveMessage);
80 | },
81 | onMessage: function (callback) {
82 | messageCallbacks.push(callback);
83 | },
84 | send: sendMessage
85 | }
86 |
87 | })();
88 |
--------------------------------------------------------------------------------
/tools/debug-client/config-example.php:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 | Switch Debug Client
11 |
12 |
13 |
14 |
15 |
16 | Please open the javascript console (:
17 |
18 | Usage:
19 |
20 | - devTool.exec( function | string ) - send a function or code string to be executed on the Switch
21 |
22 |
23 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/tools/debug-client/debugger/js/devtool.js:
--------------------------------------------------------------------------------
1 | window.devTool = (function () {
2 |
3 | function exec(input) {
4 | var code;
5 | switch (typeof input) {
6 | case "function":
7 | code = "window.TEMP_FN = " + input.toString();
8 | break;
9 | default:
10 | code = input;
11 | break;
12 | }
13 | socket.send("EXECUTE_CODE", code);
14 | };
15 |
16 | var messageHandlers = {
17 | "CONSOLE_LOG": function (message) {
18 | console.log.apply(console, message.content);
19 | },
20 | "CONSOLE_WARN": function (message) {
21 | console.warn.apply(console, message.content);
22 | },
23 | "CONSOLE_INFO": function (message) {
24 | console.info.apply(console, message.content);
25 | },
26 | "CONSOLE_ERROR": function (message) {
27 | console.error.apply(console, message.content);
28 | },
29 | "CONSOLE_DEBUG": function (message) {
30 | console.debug.apply(console, message.content);
31 | }
32 | }
33 |
34 | socket.onMessage(function (message) {
35 | if (messageHandlers.hasOwnProperty(message.type)) {
36 | var handler = messageHandlers[message.type];
37 | handler(message);
38 | }
39 | });
40 |
41 | return {
42 | socket: socket,
43 | exec: exec,
44 | }
45 |
46 | })();
47 |
--------------------------------------------------------------------------------
/tools/debug-client/switch/index.php:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 | Switch Debug Client
12 |
13 |
14 |
15 |
16 |
17 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/tools/debug-client/switch/js/devtool.js:
--------------------------------------------------------------------------------
1 | window.devTool = (function () {
2 |
3 | // execute a string as javascript code, pass the result to callback
4 | function exec(str) {
5 | var result = eval(str);
6 | if (window.TEMP_FN) {
7 | result = window.TEMP_FN();
8 | window.TEMP_FN = null;
9 | }
10 | if (result !== null) console.log(result);
11 | };
12 |
13 | // get the object at a given path, ie "window.navigator"
14 | function get(path) {
15 | return path.split(".").reduce(function(prev, curr) {
16 | return prev ? prev[curr] : undefined
17 | }, window);
18 | };
19 |
20 | // util to override a function while still maintaining the default bevahiour
21 | function overrideFn(nativeCtx, native, override) {
22 | return function() {
23 | override.call(arguments);
24 | native.apply(nativeCtx, arguments);
25 | }
26 | };
27 |
28 | /* OVERRIDE DEFAULT DEV TOOLS */
29 |
30 | function argListToArray (argList) {
31 | return Array.prototype.slice.call(argList);
32 | };
33 |
34 | var consoleOverrides = {
35 | log: function (args) {
36 | socket.send("CONSOLE_LOG", args);
37 | },
38 | warn: function () {
39 | socket.send("CONSOLE_WARN", argListToArray(arguments));
40 | },
41 | info: function () {
42 | socket.send("CONSOLE_INFO", argListToArray(arguments));
43 | },
44 | error: function () {
45 | socket.send("CONSOLE_ERROR", argListToArray(arguments));
46 | },
47 | };
48 |
49 | console.log = (function() {
50 | var log = console.log;
51 | return function() {
52 | log.apply(console, arguments);
53 | consoleOverrides.log(argListToArray(arguments));
54 | }
55 | })();
56 |
57 | console.warn = (function() {
58 | var warn = console.warn;
59 | return function() {
60 | warn.apply(console, arguments);
61 | consoleOverrides.warn(argListToArray(arguments));
62 | }
63 | })();
64 |
65 | console.info = (function() {
66 | var info = console.info;
67 | return function() {
68 | info.apply(console, arguments);
69 | consoleOverrides.info(argListToArray(arguments));
70 | }
71 | })();
72 |
73 | console.error = (function() {
74 | var error = console.error;
75 | return function() {
76 | error.apply(console, arguments);
77 | consoleOverrides.error(argListToArray(arguments));
78 | }
79 | })();
80 |
81 | var messageHandlers = {
82 | "EXECUTE_CODE": function (message) {
83 | exec(message.content);
84 | },
85 | }
86 |
87 | socket.onMessage(function (message) {
88 | if (messageHandlers.hasOwnProperty(message.type)) {
89 | var handler = messageHandlers[message.type];
90 | handler(message);
91 | }
92 | });
93 |
94 | return {
95 | console: consoleOverrides,
96 | exec: exec,
97 | get: get
98 | };
99 |
100 | })();
101 |
--------------------------------------------------------------------------------
/tools/debug-server/.gitignore:
--------------------------------------------------------------------------------
1 | error_log
2 | vendor*
3 | config.php
4 | websocket_server.pid
5 | debugger.log
6 |
--------------------------------------------------------------------------------
/tools/debug-server/DebugServer.php:
--------------------------------------------------------------------------------
1 | "SwitchBrowserDebugDaemonHandler",
12 | 'eventDriver' => 'socket_select', //'event',
13 | 'pid' => __DIR__ . '/websocket_server.pid',
14 | 'websocket' => 'tcp://' . $websocket_config['host'] . ':' . $websocket_config['port'],
15 | );
16 |
17 | $vendor = require('vendor/autoload.php');
18 | $vendor->add("SwitchBrowserDebugDaemonHandler",__DIR__);
19 | $server = new morozovsk\websocket\Server($config);
20 | echo "Starting server on " . $config["websocket"] . "\n";
21 | call_user_func(array($server, $argv[1]));
22 |
23 |
--------------------------------------------------------------------------------
/tools/debug-server/SwitchBrowserDebugDaemonHandler.php:
--------------------------------------------------------------------------------
1 | clientRoles[$connectionID] = "new"; // Not registered as a debugger or debuggee
13 | echo "{$connectionID} opened, total connections: ".count($this->clientRoles) ."\n";
14 | }
15 |
16 | protected function onClose($connectionID) {
17 | //call when existing client close connection
18 | switch ($this->clientRoles[$connectionID]) {
19 |
20 | case "new":
21 | unset($this->clientRoles[$connectionID]);
22 | echo "Connection (ID: {$connectionID}) closed before handshake received\n";
23 | break;
24 |
25 | case "debuggee":
26 | $this->debuggee = null;
27 | unset($this->clientRoles[$connectionID]);
28 | foreach ($this->debuggers as $debugger) {
29 | $this->sendToClient($debugger, json_encode(array("type" => "STATUS", "contentType" => "string", "content" => "DEBUGGEE_DISCONNECTED")));
30 | }
31 | echo "Connection with debuggee (ID: {$connectionID}) closed\n";
32 | break;
33 |
34 | case "debugger":
35 | unset($this->debuggers[$connectionID]);
36 | unset($this->clientRoles[$connectionID]);
37 | echo "Connection with debugger (ID: {$connectionID}) closed\n";
38 | break;
39 | default:
40 | echo "Connection (ID: {$connectionID}) closed\n";
41 | break;
42 | }
43 |
44 | }
45 |
46 | protected function onMessage($connectionID, $data, $type) {
47 | //call when new message from existing client
48 | $message = $data; //"user #{$connectionId}: $data";
49 | echo "$connectionID: $message\n";
50 | if ($this->clientRoles[$connectionID] == "new") {
51 | // TODO: Test to see if lock is needed
52 | switch ($message) {
53 |
54 | case "REGISTER_DEBUGGEE":
55 |
56 | if (!is_null($this->debuggee)) {
57 | // Deny attempt to register debuggee.
58 | $this->sendToClient($connectionID, json_encode(array("type" => "STATUS", "contentType" => "string", "content" => "ERROR_DEBUGGEE_EXISTS")));
59 | echo "ERROR_DEBUGGEE_EXISTS: Connection $connectionID attempted to register as the debuggee but failed\n";
60 | } else {
61 | $this->debuggee = $connectionID;
62 | $this->clientRoles[$connectionID] = "debuggee";
63 | $this->sendToClient($connectionID, json_encode(array("type" => "STATUS", "contentType" => "string", "content" => "SUCCESS_DEBUGGEE_REGISTERED")));
64 | echo "SUCCESS_DEBUGGEE_REGISTERED: Connection $connectionID registered as the debuggee\n";
65 |
66 | }
67 | return;
68 | break;
69 |
70 | case "REGISTER_DEBUGGER":
71 |
72 | $this->clientRoles[$connectionID] = "debugger";
73 | $this->debuggers[$connectionID] = $connectionID;
74 | $this->sendToClient($connectionID, json_encode(array("type" => "STATUS", "contentType" => "string", "content" => "SUCCESS_DEBUGGER_REGISTERED")));
75 | echo "SUCCESS_DEBUGGER_REGISTERED: Connection $connectionID registered as a debugger\n";
76 |
77 | return;
78 | break;
79 |
80 | default:
81 |
82 | $this->sendToClient($connectionID, json_encode(array("type" => "STATUS", "contentType" => "string", "content" => "ERROR_INVALID_COMMAND")));
83 | echo "ERROR_INVALID_COMMAND: Connection $connectionID issued an invalid command.\n";
84 |
85 | return;
86 | break;
87 | }
88 | } elseif ($this->clientRoles[$connectionID] == "debuggee") {
89 |
90 | foreach($this->debuggers as $debuggerID) {
91 | $this->sendToClient($debuggerID, $message); // TODO: Prefix to indicate message source?
92 | }
93 | } elseif ($this->clientRoles[$connectionID] == "debugger") {
94 |
95 | $this->sendToClient($this->debuggee, $message);
96 | foreach($this->debuggers as $debuggerID) {
97 | if ($debuggerID != $connectionID) {
98 | // $this->sendToClient($debuggerID, "Debugger $connectionID: $message");
99 | }
100 | }
101 | }
102 |
103 | }
104 | }
105 |
106 |
--------------------------------------------------------------------------------
/tools/debug-server/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require": {
3 | "morozovsk/websocket": "^4.2"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/tools/debug-server/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "ac8aeae3daa0ef76164a35e77fb97d4c",
8 | "packages": [
9 | {
10 | "name": "morozovsk/websocket",
11 | "version": "4.2.2",
12 | "source": {
13 | "type": "git",
14 | "url": "https://github.com/morozovsk/websocket.git",
15 | "reference": "034a514ec64eed9be3cb6fc9f4b22f67216f71ab"
16 | },
17 | "dist": {
18 | "type": "zip",
19 | "url": "https://api.github.com/repos/morozovsk/websocket/zipball/034a514ec64eed9be3cb6fc9f4b22f67216f71ab",
20 | "reference": "034a514ec64eed9be3cb6fc9f4b22f67216f71ab",
21 | "shasum": ""
22 | },
23 | "require": {
24 | "ext-mbstring": "*",
25 | "php": ">=5.3.0"
26 | },
27 | "type": "library",
28 | "autoload": {
29 | "psr-4": {
30 | "morozovsk\\websocket\\": ""
31 | }
32 | },
33 | "notification-url": "https://packagist.org/downloads/",
34 | "license": [
35 | "MIT"
36 | ],
37 | "authors": [
38 | {
39 | "name": "simple chat (single daemon)",
40 | "homepage": "http://sharoid.ru/chat.html"
41 | },
42 | {
43 | "name": "pro chat (master + worker)",
44 | "homepage": "http://sharoid.ru/chat2.html"
45 | },
46 | {
47 | "name": "simple game",
48 | "homepage": "http://sharoid.ru/game.html"
49 | }
50 | ],
51 | "description": "simple php websocket server with examples and demo: simple chat (single daemon) - http://sharoid.ru/chat.html , pro chat (master + worker) - http://sharoid.ru/chat2.html , simple game - http://sharoid.ru/game.html",
52 | "homepage": "http://sharoid.ru/game.html",
53 | "keywords": [
54 | "chat",
55 | "demo",
56 | "event",
57 | "examle",
58 | "game",
59 | "laravel",
60 | "libevent",
61 | "server",
62 | "socket_select",
63 | "websocket",
64 | "yii"
65 | ],
66 | "time": "2017-02-24T10:13:40+00:00"
67 | }
68 | ],
69 | "packages-dev": [],
70 | "aliases": [],
71 | "minimum-stability": "stable",
72 | "stability-flags": [],
73 | "prefer-stable": false,
74 | "prefer-lowest": false,
75 | "platform": [],
76 | "platform-dev": []
77 | }
78 |
--------------------------------------------------------------------------------
/tools/debug-server/config-example.php:
--------------------------------------------------------------------------------
1 |