├── .babelrc
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .travis.yml
├── .vscode
├── launch.json
└── settings.json
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── build-app.js
├── build
├── img
│ ├── api.png
│ ├── arrow.svg
│ ├── boss_rush.png
│ ├── browse.svg
│ ├── cancel.svg
│ ├── cancel_yellow.svg
│ ├── connect.svg
│ ├── courses
│ │ ├── 00-castle.png
│ │ ├── 01-bob.png
│ │ ├── 02-wf.png
│ │ ├── 03-jrb.png
│ │ ├── 04-ccm.png
│ │ ├── 05-bbh.png
│ │ ├── 06-hmc.png
│ │ ├── 07-lll.png
│ │ ├── 08-ssl.png
│ │ ├── 09-ddd.png
│ │ ├── 10-sl.png
│ │ ├── 11-wdw.png
│ │ ├── 12-ttm.png
│ │ ├── 13-thi.png
│ │ ├── 14-ttc.png
│ │ ├── 15-rr.png
│ │ ├── 16-bitdw.png
│ │ ├── 17-bitfs.png
│ │ ├── 18-bits.png
│ │ ├── 19-pss.png
│ │ ├── 20-metal.png
│ │ ├── 21-wing.png
│ │ ├── 22-vanish.png
│ │ ├── 23-otr.png
│ │ └── 24-aqua.png
│ ├── delete.png
│ ├── disconnect.svg
│ ├── discord.svg
│ ├── goomba.png
│ ├── help.png
│ ├── home.svg
│ ├── host.svg
│ ├── icon.ico
│ ├── interactionless.svg
│ ├── kirby.png
│ ├── knuckles.png
│ ├── lock.svg
│ ├── luigi.png
│ ├── mario.png
│ ├── menu.png
│ ├── menu.svg
│ ├── menu_yellow.svg
│ ├── n64.svg
│ ├── net64.svg
│ ├── pc.svg
│ ├── peach.png
│ ├── pj64_help1.png
│ ├── pj64_help2.png
│ ├── prop_hunt.svg
│ ├── reddit.svg
│ ├── regular.svg
│ ├── rosalina.png
│ ├── server.svg
│ ├── settings.svg
│ ├── shooter.svg
│ ├── sonic.png
│ ├── submit.png
│ ├── tag.svg
│ ├── toad.png
│ ├── waluigi.png
│ ├── wario.png
│ ├── wario_ware.png
│ ├── warning.svg
│ └── yoshi.png
├── patches
│ ├── 253710
│ ├── 277184
│ ├── 277308
│ ├── 277520
│ ├── 279854
│ ├── 800000
│ ├── 840000
│ ├── 862000
│ ├── 884000
│ ├── 909000
│ ├── 2773f8
│ ├── 27761c
│ ├── 27770c
│ ├── 2777c4
│ ├── 2794b0
│ ├── 29d35c
│ ├── 37a550
│ ├── 37eb80
│ ├── 8a4000
│ ├── 8c4000
│ ├── 8e4000
│ ├── 9b0000
│ ├── 9b4000
│ ├── 9b4200
│ ├── 9ce000
│ ├── 9e5600
│ ├── ff9400
│ ├── ff9800
│ ├── ff9d00
│ ├── ff9d80
│ ├── ff9e80
│ ├── ff9f00
│ ├── ff9f40
│ ├── ff9f80
│ ├── ffa000
│ └── fff000
├── processlist.node
├── styles
│ ├── smme.ttf
│ └── smme.woff
└── winprocess.node
├── compat-list.js
├── jest-browser-setup.js
├── mocks
└── WinProcess.ts
├── package.json
├── proto
├── ClientServerMessage.d.ts
├── ClientServerMessage.js
├── ClientServerMessage.proto
├── NETWORKING.md
├── ServerClientMessage.d.ts
├── ServerClientMessage.js
├── ServerClientMessage.proto
├── client-server
│ ├── Authenticate.proto
│ ├── ClientHandshake.proto
│ └── ClientServer.proto
├── server-client
│ ├── ConnectionDenied.proto
│ ├── PlayerUpdate.proto
│ ├── ServerClient.proto
│ ├── ServerHandshake.proto
│ └── ServerMessage.proto
└── shared
│ ├── Chat.proto
│ ├── Compression.proto
│ ├── GameMode.proto
│ ├── MetaData.proto
│ ├── Ping.proto
│ ├── Player.proto
│ └── PlayerData.proto
├── src
├── asm
│ ├── 909000.txt
│ ├── characterconverter.txt
│ ├── ff9800.txt
│ ├── ff9d00.txt
│ ├── ff9d80.txt
│ └── ffa000.txt
├── declarations
│ ├── discord-rich-presence.d.ts
│ └── winprocess.ts
├── main
│ ├── Connection.ts
│ ├── Connector.ts
│ ├── Emulator.ts
│ ├── HotkeyManager.ts
│ └── index.ts
├── models
│ ├── Emulator.mock.ts
│ ├── Emulator.model.ts
│ ├── Message.model.ts
│ ├── Release.model.ts
│ ├── Server.model.ts
│ └── State.model.ts
├── renderer
│ ├── Connector.ts
│ ├── GamepadManager.ts
│ ├── Request.ts
│ ├── actions
│ │ ├── chat.ts
│ │ ├── connection.ts
│ │ ├── emulator.ts
│ │ ├── models
│ │ │ ├── chat.model.ts
│ │ │ ├── connection.model.ts
│ │ │ ├── emulator.model.ts
│ │ │ ├── router.model.ts
│ │ │ ├── save.model.ts
│ │ │ ├── server.model.ts
│ │ │ └── snackbar.model.ts
│ │ ├── save.ts
│ │ ├── server.ts
│ │ └── snackbar.ts
│ ├── components
│ │ ├── areas
│ │ │ ├── ChatArea.tsx
│ │ │ ├── ConnectArea.tsx
│ │ │ ├── ConnectionArea.scss
│ │ │ ├── ConnectionArea.tsx
│ │ │ ├── HostArea.scss
│ │ │ ├── HostArea.tsx
│ │ │ ├── NavigationArea.scss
│ │ │ ├── NavigationArea.tsx
│ │ │ ├── SendPasswordArea.scss
│ │ │ ├── SendPasswordArea.tsx
│ │ │ ├── ServerArea.scss
│ │ │ ├── ServerArea.tsx
│ │ │ ├── ServerHostArea.scss
│ │ │ ├── ServerHostArea.tsx
│ │ │ ├── TopBarArea.scss
│ │ │ └── TopBarArea.tsx
│ │ ├── buttons
│ │ │ ├── HotkeyButton.scss
│ │ │ ├── HotkeyButton.tsx
│ │ │ ├── NavigationButton.scss
│ │ │ ├── NavigationButton.tsx
│ │ │ ├── SMMButton.scss
│ │ │ ├── SMMButton.tsx
│ │ │ └── ToggleButton.tsx
│ │ ├── dialogs
│ │ │ ├── NewVersionDialog.scss
│ │ │ └── NewVersionDialog.tsx
│ │ ├── forms
│ │ │ ├── HostForm.scss
│ │ │ └── HostForm.tsx
│ │ ├── headers
│ │ │ ├── HostHeader.scss
│ │ │ └── HostHeader.tsx
│ │ ├── helpers
│ │ │ ├── ExternalLink.tsx
│ │ │ ├── ProgressSpinner.scss
│ │ │ └── ProgressSpinner.tsx
│ │ ├── panels
│ │ │ ├── ChatMessagePanel.tsx
│ │ │ ├── ConsolePanel.scss
│ │ │ ├── ConsolePanel.tsx
│ │ │ ├── RadarPanel.scss
│ │ │ ├── RadarPanel.tsx
│ │ │ ├── ServerPanel.scss
│ │ │ ├── ServerPanel.tsx
│ │ │ ├── SnackbarPanel.scss
│ │ │ ├── SnackbarPanel.tsx
│ │ │ ├── WarningPanel.scss
│ │ │ └── WarningPanel.tsx
│ │ └── views
│ │ │ ├── AboutView.tsx
│ │ │ ├── AppView.tsx
│ │ │ ├── BrowseView.scss
│ │ │ ├── BrowseView.tsx
│ │ │ ├── ConnectView.scss
│ │ │ ├── ConnectView.tsx
│ │ │ ├── EmulatorView.tsx
│ │ │ ├── FaqView.tsx
│ │ │ ├── HostView.scss
│ │ │ ├── HostView.tsx
│ │ │ ├── MainView.tsx
│ │ │ ├── SettingsView.scss
│ │ │ └── SettingsView.tsx
│ ├── index.tsx
│ ├── middlewares
│ │ ├── server-middleware.ts
│ │ └── snackbar-middleware.ts
│ ├── reducers
│ │ ├── chat.ts
│ │ ├── connection.ts
│ │ ├── emulator.ts
│ │ ├── index.ts
│ │ ├── router.ts
│ │ ├── save.ts
│ │ ├── server.ts
│ │ └── snackbar.ts
│ ├── template.html
│ └── utils
│ │ ├── chat.util.ts
│ │ └── helper.util.ts
├── styles
│ └── global.scss
└── utils
│ └── Buffer.util.ts
├── tsconfig.json
├── webpack.config.js
├── webpack.dev.js
├── webpack.prod.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/env", {
4 | "targets": {
5 | "electron": "2.0.17"
6 | },
7 | "corejs": "3.6.4",
8 | "useBuiltIns": "usage"
9 | }],
10 | ["@babel/preset-typescript", {
11 | "isTSX": true,
12 | "allExtensions": true
13 | }],
14 | "@babel/react"
15 | ],
16 | "plugins": [
17 | "@babel/syntax-dynamic-import",
18 | "@babel/proposal-class-properties",
19 | "@babel/proposal-object-rest-spread"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | proto/*
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "@typescript-eslint/parser",
3 | "extends": [
4 | "eslint:recommended",
5 | "plugin:@typescript-eslint/eslint-recommended",
6 | "plugin:@typescript-eslint/recommended",
7 | "plugin:react/recommended",
8 | "standard-with-typescript"
9 | ],
10 | "plugins": [
11 | "@typescript-eslint",
12 | "react",
13 | "jsdoc"
14 | ],
15 | "parserOptions": {
16 | "ecmaVersion": 6,
17 | "sourceType": "module",
18 | "ecmaFeatures": {
19 | "jsx": true,
20 | "modules": true
21 | },
22 | "project": "./tsconfig.json"
23 | },
24 | "rules": {
25 | "@typescript-eslint/explicit-function-return-type": 0,
26 | "@typescript-eslint/default-param-last": 0,
27 | "@typescript-eslint/member-delimiter-style": [2, {
28 | "multiline": {
29 | "delimiter": "none",
30 | "requireLast": false
31 | },
32 | "singleline": {
33 | "delimiter": "comma",
34 | "requireLast": false
35 | },
36 | "overrides": {
37 | "typeLiteral": {
38 | "multiline": {
39 | "delimiter": "comma",
40 | "requireLast": true
41 | }
42 | }
43 | }
44 | }],
45 | "@typescript-eslint/no-empty-function": 1,
46 | "@typescript-eslint/no-floating-promises": 1,
47 | "@typescript-eslint/no-misused-promises": 1,
48 | "@typescript-eslint/no-non-null-assertion": 1,
49 | "@typescript-eslint/prefer-nullish-coalescing": 1,
50 | "@typescript-eslint/promise-function-async": 0,
51 | "@typescript-eslint/restrict-plus-operands": 1,
52 | "@typescript-eslint/restrict-template-expressions": 0,
53 | "@typescript-eslint/strict-boolean-expressions": 0,
54 | "max-len": [2, { "code": 120 }],
55 | "no-case-declarations": 0,
56 | "no-empty": [2, { "allowEmptyCatch": true }],
57 | "no-unused-vars": 1,
58 | "react/prop-types": 0,
59 | "react/jsx-no-target-blank": 0,
60 | "jsdoc/check-param-names": 1,
61 | "jsdoc/check-tag-names": 1,
62 | "jsdoc/check-types": 1,
63 | "jsdoc/newline-after-description": 1,
64 | "jsdoc/require-hyphen-before-param-description": 1,
65 | "jsdoc/require-param": 1,
66 | "jsdoc/require-param-description": 1,
67 | "jsdoc/require-param-name": 1,
68 | "jsdoc/require-param-type": 1,
69 | "jsdoc/require-returns-description": 1,
70 | "jsdoc/require-returns-type": 1
71 | },
72 | "env": {
73 | "browser": true,
74 | "worker": true,
75 | "jest": true
76 | },
77 | "settings": {
78 | "react": {
79 | "version": "detect"
80 | }
81 | }
82 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .credentials
2 | /.idea/
3 | /node_modules/
4 | /build/*
5 | !/build/patches/
6 | !/build/img/
7 | !/build/styles/
8 | /build/styles/renderer.*
9 | !/build/winprocess.node
10 | !/build/processlist.node
11 | /release/
12 | yarn-error.log
13 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 12.14.1
4 |
5 | env:
6 | global:
7 | - CXX=g++-4.8
8 |
9 | addons:
10 | apt:
11 | sources:
12 | - ubuntu-toolchain-r-test
13 | packages:
14 | - g++-4.8
15 |
16 | jobs:
17 | include:
18 | - stage: Lint
19 | script: yarn lint
20 | name: "Lint"
21 | # - script: yarn test
22 | # name: "Unit Tests"
23 | - stage: Build
24 | script: yarn build
25 | name: "Build"
26 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Debug Main Process",
6 | "type": "node",
7 | "request": "launch",
8 | "cwd": "${workspaceRoot}",
9 | "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
10 | "windows": {
11 | "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
12 | },
13 | "program": "${workspaceRoot}/build/index.js",
14 | "protocol": "inspector"
15 | },
16 | {
17 | "name": "Debug Renderer Process",
18 | "type": "chrome",
19 | "request": "launch",
20 | "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
21 | "windows": {
22 | "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
23 | },
24 | "runtimeArgs": [
25 | "http://localhost:4000",
26 | "--remote-debugging-port=9222"
27 | ],
28 | "sourceMaps": true,
29 | "webRoot": "${workspaceRoot}"
30 | }
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.formatOnSave": false,
3 | "editor.codeActionsOnSave": {
4 | "source.fixAll.eslint": true
5 | },
6 | "typescript.tsdk": "node_modules/typescript/lib"
7 | }
8 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | First off, thanks for taking the time to contribute!
2 |
3 | # How to contribute to Net64+
4 |
5 | Here are some rules and guidelines for contributing.
6 |
7 | ## Make an issue
8 |
9 | You should always use GitHub issues to talk about things that you want to be implemented or bugs that you found.
10 | If you are just writing in some chat app like Discord, it is very likely that noone who is responsible for the code will ever see it, so please use GitHub as a discussion platform.
11 |
12 | #### Did you find a bug?
13 |
14 | - Check our issue section, if there is already an opened issue and give your feedback
15 | - If there is not yet an issue, please don't hesitate opening a new one
16 |
17 | #### Do you want to have a new feature?
18 |
19 | Basically the same
20 |
21 | #### Do you want to make your own code changes?
22 |
23 | Before you start coding with the hope that your code will be merged, you should start a discussion about what you want to implement. Maybe we don't want or need it for some reason and we don't want you to work for nothing.
24 |
25 | ## Commit messages
26 |
27 | - Try to describe your changes as closely as possible
28 | - If there is an issue related to your changes, name it in your commit header
29 |
30 | ## Clean Code
31 |
32 | We encourage you to read  and use it as a guideline while coding.
33 |
34 | ## Styleguide
35 |
36 | We use .
37 |
38 | ESLint is set up to help you out on all linting rules.
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017-2019 Mario Reder
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Net64+ Client
2 |
3 | 
4 | 
5 | [](https://lgtm.com/projects/g/Tarnadas/net64plus)
6 | [](https://discord.gg/GgGUKH8)
7 | [](https://travis-ci.org/Tarnadas/net64plus)
8 |
9 | Net64 aka SM64O allows playing Super Mario 64 in an online multiplayer mode.
10 | Net64+ is the official continuation of the program and features an integrated server list.
11 |
12 | This repository includes the client software. If you want to host your own dedicated server, please visit the [server repository](https://github.com/Tarnadas/net64plus-server).
13 |
14 | ## Download
15 |
16 | Download the client/server/emulator bundle in the [release section](https://github.com/Tarnadas/net64plus/releases).
17 |
18 | ## Server List
19 |
20 | There is a [public server list](https://net64-mod.github.io/servers) of all Net64+ server, that have enabled listing.
21 |
--------------------------------------------------------------------------------
/build-app.js:
--------------------------------------------------------------------------------
1 | const packager = require('electron-packager')
2 | const rimraf = require('rimraf')
3 | const fs = require('fs')
4 | const path = require('path')
5 |
6 | const packageJson = JSON.parse(fs.readFileSync('./package.json'))
7 | const out = path.normalize(`./release/${packageJson.version}`)
8 | if (!fs.existsSync('./release')) {
9 | fs.mkdirSync('./release')
10 | }
11 | if (!fs.existsSync(out)) {
12 | fs.mkdirSync(out)
13 | }
14 |
15 | packager({
16 | name: 'Net64+',
17 | dir: './build',
18 | out,
19 | arch: 'x64',
20 | platform: 'win32',
21 | appVersion: packageJson.version,
22 | icon: './build/img/icon.ico',
23 | overwrite: true
24 | }, (err, appPaths) => {
25 | if (err) throw err
26 | fs.writeFileSync(path.join(appPaths[0], 'resources/app/package.json'), JSON.stringify(packageJson))
27 | fs.mkdirSync(path.join(appPaths[0], `patches`))
28 | fs.readdirSync('./build/patches').map(val => `./build/patches/${val}`).forEach(file => {
29 | fs.createReadStream(file).pipe(fs.createWriteStream(path.join(appPaths[0], `patches/${file.split('patches/')[1]}`)))
30 | })
31 | rimraf(path.join(appPaths[0], 'resources/app/patches'), () => {})
32 | })
33 |
--------------------------------------------------------------------------------
/build/img/api.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/api.png
--------------------------------------------------------------------------------
/build/img/arrow.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/img/boss_rush.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/boss_rush.png
--------------------------------------------------------------------------------
/build/img/browse.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/img/cancel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/img/cancel_yellow.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/img/connect.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/img/courses/00-castle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/00-castle.png
--------------------------------------------------------------------------------
/build/img/courses/01-bob.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/01-bob.png
--------------------------------------------------------------------------------
/build/img/courses/02-wf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/02-wf.png
--------------------------------------------------------------------------------
/build/img/courses/03-jrb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/03-jrb.png
--------------------------------------------------------------------------------
/build/img/courses/04-ccm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/04-ccm.png
--------------------------------------------------------------------------------
/build/img/courses/05-bbh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/05-bbh.png
--------------------------------------------------------------------------------
/build/img/courses/06-hmc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/06-hmc.png
--------------------------------------------------------------------------------
/build/img/courses/07-lll.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/07-lll.png
--------------------------------------------------------------------------------
/build/img/courses/08-ssl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/08-ssl.png
--------------------------------------------------------------------------------
/build/img/courses/09-ddd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/09-ddd.png
--------------------------------------------------------------------------------
/build/img/courses/10-sl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/10-sl.png
--------------------------------------------------------------------------------
/build/img/courses/11-wdw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/11-wdw.png
--------------------------------------------------------------------------------
/build/img/courses/12-ttm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/12-ttm.png
--------------------------------------------------------------------------------
/build/img/courses/13-thi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/13-thi.png
--------------------------------------------------------------------------------
/build/img/courses/14-ttc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/14-ttc.png
--------------------------------------------------------------------------------
/build/img/courses/15-rr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/15-rr.png
--------------------------------------------------------------------------------
/build/img/courses/16-bitdw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/16-bitdw.png
--------------------------------------------------------------------------------
/build/img/courses/17-bitfs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/17-bitfs.png
--------------------------------------------------------------------------------
/build/img/courses/18-bits.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/18-bits.png
--------------------------------------------------------------------------------
/build/img/courses/19-pss.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/19-pss.png
--------------------------------------------------------------------------------
/build/img/courses/20-metal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/20-metal.png
--------------------------------------------------------------------------------
/build/img/courses/21-wing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/21-wing.png
--------------------------------------------------------------------------------
/build/img/courses/22-vanish.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/22-vanish.png
--------------------------------------------------------------------------------
/build/img/courses/23-otr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/23-otr.png
--------------------------------------------------------------------------------
/build/img/courses/24-aqua.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/courses/24-aqua.png
--------------------------------------------------------------------------------
/build/img/delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/delete.png
--------------------------------------------------------------------------------
/build/img/disconnect.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/img/discord.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/build/img/goomba.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/goomba.png
--------------------------------------------------------------------------------
/build/img/help.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/help.png
--------------------------------------------------------------------------------
/build/img/home.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/img/host.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/img/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/icon.ico
--------------------------------------------------------------------------------
/build/img/interactionless.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/img/kirby.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/kirby.png
--------------------------------------------------------------------------------
/build/img/knuckles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/knuckles.png
--------------------------------------------------------------------------------
/build/img/lock.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/img/luigi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/luigi.png
--------------------------------------------------------------------------------
/build/img/mario.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/mario.png
--------------------------------------------------------------------------------
/build/img/menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/menu.png
--------------------------------------------------------------------------------
/build/img/menu.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/img/menu_yellow.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/img/n64.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/img/pc.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/img/peach.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/peach.png
--------------------------------------------------------------------------------
/build/img/pj64_help1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/pj64_help1.png
--------------------------------------------------------------------------------
/build/img/pj64_help2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/pj64_help2.png
--------------------------------------------------------------------------------
/build/img/reddit.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/img/regular.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/img/rosalina.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/rosalina.png
--------------------------------------------------------------------------------
/build/img/server.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/img/settings.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/img/shooter.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/img/sonic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/sonic.png
--------------------------------------------------------------------------------
/build/img/submit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/submit.png
--------------------------------------------------------------------------------
/build/img/toad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/toad.png
--------------------------------------------------------------------------------
/build/img/waluigi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/waluigi.png
--------------------------------------------------------------------------------
/build/img/wario.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/wario.png
--------------------------------------------------------------------------------
/build/img/wario_ware.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/wario_ware.png
--------------------------------------------------------------------------------
/build/img/warning.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/img/yoshi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/img/yoshi.png
--------------------------------------------------------------------------------
/build/patches/253710:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/253710
--------------------------------------------------------------------------------
/build/patches/277184:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/patches/277308:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/patches/2773f8:
--------------------------------------------------------------------------------
1 | 6
--------------------------------------------------------------------------------
/build/patches/277520:
--------------------------------------------------------------------------------
1 | $
--------------------------------------------------------------------------------
/build/patches/27761c:
--------------------------------------------------------------------------------
1 | (
--------------------------------------------------------------------------------
/build/patches/27770c:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/patches/2777c4:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/patches/2794b0:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/patches/279854:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/patches/29d35c:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/29d35c
--------------------------------------------------------------------------------
/build/patches/37a550:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/37a550
--------------------------------------------------------------------------------
/build/patches/37eb80:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/37eb80
--------------------------------------------------------------------------------
/build/patches/800000:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/800000
--------------------------------------------------------------------------------
/build/patches/840000:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/840000
--------------------------------------------------------------------------------
/build/patches/862000:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/862000
--------------------------------------------------------------------------------
/build/patches/884000:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/884000
--------------------------------------------------------------------------------
/build/patches/8a4000:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/8a4000
--------------------------------------------------------------------------------
/build/patches/8c4000:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/8c4000
--------------------------------------------------------------------------------
/build/patches/8e4000:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/8e4000
--------------------------------------------------------------------------------
/build/patches/909000:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/909000
--------------------------------------------------------------------------------
/build/patches/9b0000:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/9b0000
--------------------------------------------------------------------------------
/build/patches/9b4000:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/9b4000
--------------------------------------------------------------------------------
/build/patches/9b4200:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/9b4200
--------------------------------------------------------------------------------
/build/patches/9ce000:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/9ce000
--------------------------------------------------------------------------------
/build/patches/9e5600:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/9e5600
--------------------------------------------------------------------------------
/build/patches/ff9400:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/ff9400
--------------------------------------------------------------------------------
/build/patches/ff9800:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/ff9800
--------------------------------------------------------------------------------
/build/patches/ff9d00:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/ff9d00
--------------------------------------------------------------------------------
/build/patches/ff9d80:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/ff9d80
--------------------------------------------------------------------------------
/build/patches/ff9e80:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/ff9e80
--------------------------------------------------------------------------------
/build/patches/ff9f00:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/ff9f00
--------------------------------------------------------------------------------
/build/patches/ff9f40:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/ff9f40
--------------------------------------------------------------------------------
/build/patches/ff9f80:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/ff9f80
--------------------------------------------------------------------------------
/build/patches/ffa000:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/ffa000
--------------------------------------------------------------------------------
/build/patches/fff000:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/patches/fff000
--------------------------------------------------------------------------------
/build/processlist.node:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/processlist.node
--------------------------------------------------------------------------------
/build/styles/smme.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/styles/smme.ttf
--------------------------------------------------------------------------------
/build/styles/smme.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/styles/smme.woff
--------------------------------------------------------------------------------
/build/winprocess.node:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tarnadas/net64plus/2793ed601dc941e22d5270850ce22cf27109b70d/build/winprocess.node
--------------------------------------------------------------------------------
/compat-list.js:
--------------------------------------------------------------------------------
1 | module.exports = function getCompatMin (currentVersion) {
2 | const [ major, minor ] = currentVersion.split('.').map(n => Number(n))
3 | if (major === 2 && minor === 1) return [ '2', '0' ]
4 | if (major === 2 && minor === 2) return [ '2', '0' ]
5 | if (major === 2 && minor === 3) return [ '2', '0' ]
6 | if (major === 2 && minor === 4) return [ '2', '0' ]
7 | if (major === 2 && minor === 5) return [ '2', '0' ]
8 | throw new Error(`Compatibility list found unknown version ${currentVersion}`)
9 | }
10 |
--------------------------------------------------------------------------------
/jest-browser-setup.js:
--------------------------------------------------------------------------------
1 | const encoding = require('text-encoding')
2 |
3 | global.TextDecoder = encoding.TextDecoder
4 | global.TextEncoder = encoding.TextEncoder
5 |
--------------------------------------------------------------------------------
/mocks/WinProcess.ts:
--------------------------------------------------------------------------------
1 | export default jest.mock('winprocess', () => ({
2 | Process: (processId: number) => ({
3 | open: () => {},
4 | readMemory: (offset: number, length: number) => {
5 | return Buffer.alloc(length)
6 | },
7 | writeMemory: (offset: number, buffer: Buffer) => {}
8 | })
9 | }))
10 |
--------------------------------------------------------------------------------
/proto/ClientServerMessage.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | import "client-server/ClientServer.proto";
4 | import "shared/Compression.proto";
5 |
6 | message ClientServerMessage {
7 | Compression compression = 1;
8 | uint32 uncompressed_size = 2;
9 | oneof message {
10 | bytes compressed_data = 3;
11 | ClientServer data = 4;
12 | }
13 | }
--------------------------------------------------------------------------------
/proto/NETWORKING.md:
--------------------------------------------------------------------------------
1 | # Net64+ Networking Protocol
2 |
3 | Net64+ >=2.0 will entirely use [Protocol Buffer](https://developers.google.com/protocol-buffers/docs/overview) for message serialization.
4 |
5 | The type of compression that will be used is still WIP.
6 | Candidates are [ZSTD](http://facebook.github.io/zstd) or GZIP. ZSTD has the advantage, that it is heavily optimized for small data compression. You need to feed it a lot of training data and it will generate an optimized dictionary, which can then be used for future compressions.
7 |
8 | ## Server to Client
9 |
10 | A server-client-message has the following definition:
11 |
12 | ```proto
13 | message ServerClientMessage {
14 | enum Compression {
15 | NONE = 0;
16 | ZSTD = 1;
17 | GZIP = 2;
18 | }
19 | Compression compression = 1;
20 | oneof message {
21 | bytes compressed_data = 2;
22 | ServerClient data = 3;
23 | }
24 | }
25 | ```
26 |
27 | Any message received from a client can potentially be compressed. The compression type can be seen in field#1. The `ServerClient` type is the uncompressed message. If any compression method was used, the compressed data can instead be decompressed and deserialized to a `ServerClient` object.
28 |
29 | #### ServerClient Object
30 |
31 | A `ServerClient` object has the following definition:
32 |
33 | ```proto
34 | message ServerClient {
35 | enum MessageType {
36 | HANDSHAKE = 0;
37 | PING = 1;
38 | SERVER_MESSAGE = 2;
39 | PLAYER_LIST_UPDATE = 3;
40 | PLAYER_UPDATE = 4;
41 | PLAYER_DATA = 128;
42 | META_DATA = 129;
43 | META_MESSAGE = 130;
44 | CHAT_MESSAGE = 131;
45 | }
46 | MessageType message_type = 1;
47 | oneof message {
48 | Handshake handshake = 2;
49 | Ping ping = 3;
50 | ServerMessage server_message = 3;
51 | PlayerListUpdate player_list_update_message = 4;
52 | PlayerUpdate player_update_message = 5;
53 | MetaData meta_data_message = 6;
54 | Chat chat_message = 7;
55 | }
56 | }
57 | ```
58 |
59 | The most significant bit of the `MessageType` enum reflects whether the message contains data that is used by the emulator.
60 |
61 | A `ServerMessage` contains data which is used by the client program to determine various client-specific tasks.
62 |
63 | A `PlayerListUpdate` contains a list of all currently connected players.
64 |
65 | A `PlayerUpdate` is a single player update, because it is more lightweight to only send the diff, if a player object changes.
66 |
67 | `PlayerData` contains an array of player data from all currently connected players, which will be sent with ~30fps.
68 |
69 | `MetaData` contains any additional data, that needs to be sent from the Emulator.
70 |
71 | `Chat` is a chat message.
72 | 
--------------------------------------------------------------------------------
/proto/ServerClientMessage.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | import "server-client/ServerClient.proto";
4 | import "shared/Compression.proto";
5 |
6 | message ServerClientMessage {
7 | Compression compression = 1;
8 | uint32 uncompressed_size = 2;
9 | oneof message {
10 | bytes compressed_data = 3;
11 | ServerClient data = 4;
12 | }
13 | }
--------------------------------------------------------------------------------
/proto/client-server/Authenticate.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | message Authenticate {
4 | string password = 1;
5 | }
--------------------------------------------------------------------------------
/proto/client-server/ClientHandshake.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | message ClientHandshake {
4 | uint32 major = 1;
5 | uint32 minor = 2;
6 | uint32 character_id = 3;
7 | string username = 4;
8 | }
--------------------------------------------------------------------------------
/proto/client-server/ClientServer.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | import "client-server/ClientHandshake.proto";
4 | import "shared/Ping.proto";
5 | import "shared/Player.proto";
6 | import "client-server/Authenticate.proto";
7 | import "shared/PlayerData.proto";
8 | import "shared/MetaData.proto";
9 | import "shared/Chat.proto";
10 |
11 | message ClientServer {
12 | enum MessageType {
13 | UNKNOWN = 0;
14 | HANDSHAKE = 2;
15 | PING = 3;
16 | PLAYER_UPDATE = 6;
17 | AUTHENTICATE = 7;
18 | PLAYER_DATA = 128;
19 | META_DATA = 129;
20 | CHAT = 130;
21 | }
22 | MessageType message_type = 1;
23 | oneof message {
24 | ClientHandshake handshake = 2;
25 | Ping ping = 3;
26 | Player player = 6;
27 | Authenticate authenticate = 7;
28 | PlayerData player_data = 128;
29 | MetaData meta_data = 129;
30 | Chat chat = 130;
31 | }
32 | }
--------------------------------------------------------------------------------
/proto/server-client/ConnectionDenied.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | message ConnectionDenied {
4 | enum Reason {
5 | SERVER_FULL = 0;
6 | WRONG_VERSION = 1;
7 | }
8 | Reason reason = 1;
9 | oneof message {
10 | ServerFull server_full = 2;
11 | WrongVersion wrong_version = 3;
12 | }
13 | }
14 |
15 | message ServerFull {
16 | uint32 max_players = 1;
17 | }
18 |
19 | message WrongVersion {
20 | uint32 major_version = 1;
21 | uint32 minor_version = 2;
22 | }
--------------------------------------------------------------------------------
/proto/server-client/PlayerUpdate.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | import "shared/Player.proto";
4 |
5 | message PlayerUpdate {
6 | uint32 player_id = 1;
7 | Player player = 2;
8 | }
9 |
10 | message PlayerListUpdate {
11 | repeated PlayerUpdate player_updates = 1;
12 | }
--------------------------------------------------------------------------------
/proto/server-client/ServerClient.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | import "server-client/ServerHandshake.proto";
4 | import "shared/Ping.proto";
5 | import "server-client/ServerMessage.proto";
6 | import "server-client/PlayerUpdate.proto";
7 | import "shared/PlayerData.proto";
8 | import "shared/MetaData.proto";
9 | import "shared/Chat.proto";
10 |
11 | message ServerClient {
12 | enum MessageType {
13 | UNKNOWN = 0;
14 | HANDSHAKE = 2;
15 | PING = 3;
16 | SERVER_MESSAGE = 4;
17 | PLAYER_LIST_UPDATE = 5;
18 | PLAYER_UPDATE = 6;
19 | PLAYER_DATA = 128;
20 | META_DATA = 129;
21 | CHAT = 130;
22 | }
23 | MessageType message_type = 1;
24 | oneof message {
25 | ServerHandshake handshake = 2;
26 | Ping ping = 3;
27 | ServerMessage server_message = 4;
28 | PlayerListUpdate player_list_update = 5;
29 | PlayerUpdate player_update = 6;
30 | PlayerData player_data = 128;
31 | MetaData meta_data = 129;
32 | Chat chat = 130;
33 | }
34 | }
--------------------------------------------------------------------------------
/proto/server-client/ServerHandshake.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | import "shared/GameMode.proto";
4 | import "server-client/PlayerUpdate.proto";
5 |
6 | message ServerHandshake {
7 | uint32 player_id = 1;
8 | string ip = 2;
9 | uint32 port = 3;
10 | string domain = 4;
11 | string name = 5;
12 | string description = 6;
13 | PlayerListUpdate player_list = 7;
14 | string country_code = 8;
15 | GameModeType game_mode = 9;
16 | bool password_required = 10;
17 | }
--------------------------------------------------------------------------------
/proto/server-client/ServerMessage.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | import "shared/GameMode.proto";
4 | import "server-client/ConnectionDenied.proto";
5 |
6 | message ServerMessage {
7 | enum MessageType {
8 | CONNECTION_DENIED = 0;
9 | GAME_MODE = 1;
10 | PLAYER_REORDER = 2;
11 | ERROR = 3;
12 | AUTHENTICATION = 4;
13 | }
14 | MessageType message_type = 1;
15 | oneof message {
16 | ConnectionDenied connection_denied = 2;
17 | GameMode game_mode = 3;
18 | PlayerReorder player_reorder = 4;
19 | Error error = 5;
20 | Authentication authentication = 6;
21 | }
22 | }
23 |
24 | message PlayerReorder {
25 | bool grant_token = 1;
26 | uint32 player_id = 2;
27 | }
28 |
29 | message Error {
30 | enum ErrorType {
31 | UNKNOWN = 0;
32 | BAD_REQUEST = 400;
33 | UNAUTHORIZED = 401;
34 | TOO_MANY_REQUESTS = 429;
35 | INTERNAL_SERVER_ERROR = 500;
36 | }
37 | ErrorType error_type = 1;
38 | string message = 2;
39 | }
40 |
41 | message Authentication {
42 | enum Status {
43 | ACCEPTED = 0;
44 | DENIED = 1;
45 | }
46 | Status status = 1;
47 | uint32 throttle = 2;
48 | }
--------------------------------------------------------------------------------
/proto/shared/Chat.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | message Chat {
4 | enum ChatType {
5 | GLOBAL = 0;
6 | PRIVATE = 1;
7 | COMMAND = 255;
8 | }
9 | ChatType chat_type = 1;
10 | uint32 sender_id = 2;
11 | string message = 3;
12 | oneof message_type {
13 | ChatGlobal global = 4;
14 | ChatPrivate private = 5;
15 | ChatCommand command = 255;
16 | }
17 | }
18 |
19 | message ChatGlobal {
20 | }
21 |
22 | message ChatPrivate {
23 | uint32 receiver_id = 1;
24 | }
25 |
26 | message ChatCommand {
27 | repeated string arguments = 1;
28 | }
--------------------------------------------------------------------------------
/proto/shared/Compression.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | enum Compression {
4 | NONE = 0;
5 | ZSTD = 1;
6 | GZIP = 2;
7 | }
--------------------------------------------------------------------------------
/proto/shared/GameMode.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | message GameMode {
4 | GameModeType game_mode = 1;
5 | }
6 | enum GameModeType {
7 | NONE = 0;
8 | DEFAULT = 1;
9 | THIRD_PERSON_SHOOTER = 2;
10 | INTERACTIONLESS = 3;
11 | PROP_HUNT = 4;
12 | BOSS_RUSH = 5;
13 | TAG = 6;
14 | WARIO_WARE = 8;
15 | }
--------------------------------------------------------------------------------
/proto/shared/MetaData.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | message Meta {
4 | uint32 length = 1;
5 | uint32 address = 2;
6 | bytes data = 3;
7 | }
8 |
9 | message MetaData {
10 | repeated Meta meta_data = 1;
11 | }
--------------------------------------------------------------------------------
/proto/shared/Ping.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | message Ping {}
--------------------------------------------------------------------------------
/proto/shared/Player.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | message Player {
4 | string username = 1;
5 | uint32 character_id = 2;
6 | }
--------------------------------------------------------------------------------
/proto/shared/PlayerData.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | message PlayerData {
4 | uint32 data_length = 1;
5 | repeated PlayerBytes player_bytes = 2;
6 | }
7 |
8 | message PlayerBytes {
9 | uint32 player_id = 1;
10 | bytes player_data = 2;
11 | }
--------------------------------------------------------------------------------
/src/asm/909000.txt:
--------------------------------------------------------------------------------
1 | .org 0x7f00
2 | !main:
3 | addiu sp, sp, $ffe4
4 | sw ra, $14 (SP)
5 | lui t0, $8039
6 | lw t2, $be28 (T0)
7 | beq t2, r0, !end
8 | nop
9 | lw t3, $0004 (T2)
10 | lui at, $8016
11 | ori at, at, $f5b0
12 | bne t3, at, !end
13 | nop
14 | lui t0, $80ff
15 | lbu t2, $5ff4 (T0)
16 | addiu at, r0, $8
17 | bne t2, at, !end
18 | nop
19 | ori t1, t0, $1b00
20 | or a3, r0, r0
21 | or t4, r0, r0
22 | !Loopcheck:
23 | lbu t3, $771a (T0)
24 | slt at, t3, a3
25 | bne at, r0, !endofloop
26 | nop
27 | lbu t4, $7700 (T0)
28 | or a3, t3, r0
29 | !endofloop:
30 | addiu t0, t0, $0100
31 | bne t0, t1, !Loopcheck
32 | nop
33 | or t5, a3, r0
34 | sw a3, $0018 (SP)
35 | or a3, t4, r0
36 | lui t0, $8091
37 | lbu t0, $02ff (T0)
38 | bne t5, t0, !noplayerwin
39 | nop
40 | addiu a0, r0, $0020
41 | addiu a1, r0, $00d1
42 | lui a2, $8091
43 | jal $2d62d8
44 | ori a2, a2, $02a0
45 | beq r0, r0, !reunition
46 | nop
47 |
48 |
49 | !noplayerwin:
50 | addiu a0, r0, $0020
51 | addiu a1, r0, $00d1
52 | lui a2, $8091
53 | jal $2d62d8
54 | ori a2, a2, $0260
55 |
56 |
57 | lw a3, $0018 (SP)
58 | addiu a0, r0, $0020
59 | addiu a1, r0, $00b9
60 | lui a2, $8091
61 | jal $2d62d8
62 | ori a2, a2, $0280
63 | !reunition:
64 | lui t0, $80ff
65 | lbu t1, $7714 (T0)
66 | lui at, $8033
67 | sh t1, $ddf8 (AT)
68 |
69 | lui t1, $8034
70 | sw r0, $afa0 (T1)
71 | jal $ffa000
72 | nop
73 | jal $910d00
74 | lui t0, $801a
75 | !end:
76 | lw ra, $14 (SP)
77 | jr ra
78 | addiu sp, sp, $1c
79 |
80 | .org 0x7d00
81 | !nops:
82 | sb r0, $7860 (T0)
83 | sb r0, $7870 (T0)
84 | sb r0, $7880 (T0)
85 | sb r0, $7890 (T0)
86 | sb r0, $78a0 (T0)
87 | sb r0, $78b0 (T0)
88 | sb r0, $78c0 (T0)
89 | sb r0, $78d0 (T0)
90 | sb r0, $78e0 (T0)
91 | sb r0, $78f0 (T0)
92 | sb r0, $7900 (T0)
93 | sb r0, $7910 (T0)
94 | sb r0, $7920 (T0)
95 | sb r0, $7930 (T0)
96 | sb r0, $7940 (T0)
97 | sb r0, $7950 (T0)
98 | sb r0, $7960 (T0)
99 | sb r0, $7970 (T0)
100 | sb r0, $7980 (T0)
101 | sb r0, $7990 (T0)
102 | sb r0, $79a0 (T0)
103 | sb r0, $79b0 (T0)
104 | sb r0, $79c0 (T0)
105 | sb r0, $79d0 (T0)
106 | sb r0, $79e0 (T0)
107 | sb r0, $79f0 (T0)
108 | sb r0, $7a00 (T0)
109 | sb r0, $7a10 (T0)
110 | sb r0, $7a20 (T0)
111 | sb r0, $7a30 (T0)
112 | sb r0, $7a40 (T0)
113 | sb r0, $7a50 (T0)
114 | sb r0, $7a60 (T0)
115 | sb r0, $7c40 (T0)
116 | jr ra
117 | sb r0, $7c50 (T0)
--------------------------------------------------------------------------------
/src/asm/characterconverter.txt:
--------------------------------------------------------------------------------
1 | //yoshi is at 80570000 to 80590E00 model size 2ac0
2 |
3 | .org 0x861c0
4 | !hook:
5 | addiu sp, sp, $ffe8
6 | sw ra, $14 (SP)
7 | jal $2ca6d0
8 | nop
9 | !endhook:
10 | lw ra, $14 (SP)
11 | jr ra
12 | addiu sp, sp, $0018
13 |
14 | .org 0x856d0
15 | !main:
16 | addiu sp, sp, $ffe8
17 | sw ra, $14 (SP)
18 | lui a2, $8040
19 | ori a2, a2, $0000 //RAM location, update 369F40 to write this in for your own characters
20 | lui t0, $800f
21 | ori t0, t0, $0860
22 | sub a3, a2, t0
23 |
24 | lui t0, $8033
25 | lw t0, $DDC4 (T0)
26 | lw t3, $0008 (T0)
27 | bne t3, r0, !nonewmodelset
28 | nop
29 | lw t1, $0004 (T0)
30 | or at, a2, r0
31 | addiu t9, at, $7ce8 //model size
32 | !loop:
33 | lw t2, $0000 (T1)
34 | lui t3, $ffff
35 | and t3, t2, t3
36 | lui t4, $800f
37 | bne t3, t4, !skipthisasbiufbau9sif
38 | nop
39 | add t2, t2, a3
40 | !skipthisasbiufbau9sif:
41 | sw t2, $0000 (AT)
42 | addiu at, at, $4
43 | addiu t1, t1, $4
44 | bne t9, at, !loop
45 | nop
46 | lui at, $8033
47 | lw at, $ddc4 (AT)
48 | or t0, a2, r0
49 | sw t0, $0008 (at)
50 | nop
51 | jal $277f50
52 | lui a0, $0400
53 | addiu a3, a2, $2000
54 | //addiu a3, a3, $4000
55 | lui at, $0003
56 | ori at, at, $6000 //bank 04 size
57 | add a1, a3, at
58 | !copyloop:
59 | lw t2, $0000 (v0)
60 | sw t2, $0000 (A3)
61 | addiu a3, a3, $4
62 | addiu v0, v0, $4
63 | bne a1, a3, !copyloop
64 | nop
65 | or a0, a2, r0
66 | addiu a1, a2, $3000 //model size again
67 | !loppsaf:
68 | lw t2, $0000 (A0)
69 | lui at, $ffff
70 | and at, at, t2
71 | lui t3, $0400
72 | beq t3, at, !fixthispointer
73 | nop
74 | lui t3, $0401
75 | beq t3, at, !fixthispointer
76 | nop
77 | lui t3, $0403
78 | beq t3, at, !fixthispointer
79 | nop
80 | lui t3, $0402
81 | bne t3, at, !dontfix
82 | nop
83 | !fixthispointer:
84 | jal $30FFF8
85 | nop
86 | !dontfix:
87 | addiu a0, a0, $4
88 | bne a0, a1, !loppsaf
89 | nop
90 |
91 | !nonewmodelset:
92 | lui a0, $8034
93 | lhu a0, $afa0 (A0)
94 | andi a0, a0, $0020
95 | beq a0, r0, !end
96 | nop
97 | addiu a1, r0, $0002
98 | lui a0, $8036
99 | lw a0, $1158 (A0)
100 | lui a2, $1300
101 | jal $29edcc
102 | addiu a2, a2, $2a48
103 | !end:
104 | lw ra, $14 (SP)
105 | jr ra
106 | addiu sp, sp, $018
107 |
108 | //803e92b0
109 | .org 0xCAFF8
110 | !fixDL:
111 | addiu sp, sp, $ffe8
112 | sw ra, $14 (SP)
113 | addiu a3, a2, $2000
114 | //addiu a3, a3, $4000
115 | lui at, $00ff
116 | ori at, at, $ffff
117 | and a3, a3, at
118 | lui at, $0400
119 | sub a3, a3, at
120 | add t2, t2, a3
121 | sw t2, $0000 (A0)
122 | lui t3, $8000
123 | or t3, t3, t2
124 | jal $3145D4
125 | nop
126 | lw ra, $14 (SP)
127 | jr ra
128 | addiu sp, sp, $0018
129 |
130 |
131 | .org 0xCF5D4
132 | !dasfihasbuf:
133 | addiu sp, sp, $ffe8
134 | sw ra, $14 (SP)
135 | !loopfixDL:
136 |
137 | lw t4, $0000 (T3)
138 | lui at, $ff00
139 | and at, at, t4
140 | lui t5, $0300
141 | beq t5, at, !fixpointer
142 | nop
143 | lui t5, $0400
144 | beq t5, at, !fixpointer
145 | nop
146 | lui t5, $fd00
147 | bne t5, at, !Notexturefix
148 | nop
149 | !fixpointer:
150 | lw t7, $0004 (t3)
151 | lui at, $ff00
152 | and at, at, t7
153 | beq at, r0, !Notexturefix
154 | nop
155 | add t7, t7, a3
156 | sw t7, $0004 (T3)
157 | !Notexturefix:
158 | sw t3, $0010 (SP)
159 | lui t5, $0600
160 | bne t5, at, !nosubcall
161 | nop
162 | lw t2, $0004 (t3)
163 | lui at, $ff00
164 | and at, at, t2
165 | beq at, r0, !nosubcall
166 | nop
167 | add t2, t2, a3
168 | sw t2, $0004 (T3)
169 | lui t3, $8000
170 | jal $3145D4
171 | or t3, t3, t2
172 | !nosubcall:
173 | lw t3, $0010 (SP)
174 | !endoftextureloop:
175 | lw t2, $0008 (T3)
176 | lui at, $b800
177 | addiu t3, t3, $0008
178 | bne t2, at, !loopfixDL
179 | nop
180 | lw ra, $14 (SP)
181 | jr ra
182 | addiu sp, sp, $0018
--------------------------------------------------------------------------------
/src/asm/ff9800.txt:
--------------------------------------------------------------------------------
1 | .org 0x0000
2 | !main:
3 | addiu sp, sp, $ffe4
4 | sw ra, $14 (SP)
5 | lui t0, $8036
6 | lw t7, $1160 (T0)
7 | lbu t2, $0188 (T7)
8 | sll t2, t2, $08
9 | lui t1, $80ff
10 | add t1, t1, t2
11 | lbu t9, $770e (T1)
12 | lhu t8, $770c (T1)
13 | andi t8, t8, $ff0f
14 | lui t6, $80ff
15 | lhu t6, $770c (T6)
16 | andi t6, t6, $ff0f
17 | bne t8, t6, !nonsolid
18 | nop
19 |
20 |
21 | lbu t1, $7701 (T1)
22 | addiu t8, r0, $0
23 | addiu at, r0, $4
24 | bne at, t9, !notwario
25 | nop
26 | addiu t8, r0, $1
27 | !notwario:
28 | lw t9, $0180 (T7)
29 | add t9, t9, t8
30 | sw t9, $0180 (t7)
31 | lui t3, $80ff
32 | lbu t8, $5ff4 (t3)
33 | addiu at, r0, $0005
34 | bne at, t8, !regularbox
35 | nop
36 | lbu at, $7718 (T3)
37 | lbu t2, $0188 (T7)
38 | bne t2, at, !regularbox
39 | nop
40 | addiu t9, t9, $3
41 | sw t9, $0180 (T7)
42 | !regularbox:
43 | sll t1, t1, $2
44 | add t0, t3, t1
45 | addiu t0, t0, $2000
46 | lw t0, $7400 (T0)
47 | ori at, r0, $8000
48 | bne t0, at, !dontset
49 | nop
50 | sw r0, $0180 (T7)
51 | lw t1, $0134 (T7)
52 | ori at, r0, $c003
53 | bne t1, at, !dontset
54 | nop
55 | lui t0, $8034
56 | lui at, $4268
57 | sw at, $b1bc (T0)
58 | lui at, $1080
59 | ori at, at, $08A4
60 | sw at, $b17c (T0)
61 | ori t0, r0, $8000
62 | !dontset:
63 | sw t0, $0130 (T7)
64 | ori at, r0, $0008
65 | bne at, t0, !noexpansion
66 | nop
67 | lui a0, $80ff
68 | lbu a0, $5ff4 (a0)
69 | addiu at, r0, $0003
70 | beq at, a0, !noexpansion
71 | nop
72 | lw t1, $00d4 (T7)
73 | sw t1, $00c8 (T7)
74 | sw t7, $0018 (SP)
75 | addiu a1, r0, $0000
76 | or a0, t7, r0
77 | lui a2, $00ff
78 | jal $29edcc
79 | ori a2, a2, $9e80
80 | lw t7, $0018 (SP)
81 | lbu t2, $0188 (T7)
82 | sb t2, $0188 (V0)
83 | !noexpansion:
84 | lui t1, $4000
85 | bne t1, t0, !end
86 | nop
87 | lui t2, $8034
88 | lwc1 f2, $b1b0 (T2)
89 | lwc1 f4, $b1bc (T2)
90 | mtc1 r0, f8
91 | c.lt.s f4, f8
92 | nop
93 | bc1f !end
94 | nop
95 | lui at, $42c8
96 | mtc1 at, f4
97 | sub.s f2, f2, f4
98 | lwc1 f6, $00a4 (T7)
99 | c.lt.s f2, f6
100 | nop
101 | bc1t !end
102 | nop
103 | addiu at, r0, $0080
104 | sw at, $0130 (T7)
105 | sw r0, $0180 (T7)
106 | !end:
107 | lui t0, $8036
108 | lw t7, $1160 (T0)
109 |
110 |
111 | lbu t2, $0188 (T7)
112 | sll t2, t2, $08
113 | lui t1, $80ff
114 | add t1, t1, t2
115 | lhu t8, $770c (T1)
116 |
117 | andi t8, t8, $0040
118 | beq t8, r0, !nonmetalasnfiunsaf
119 | nop
120 | addiu at, r0, $0008
121 | sw at, $0130 (T7)
122 | !nonmetalasnfiunsaf:
123 | lw t1, $0134 (T7)
124 | ori at, r0, $a000
125 | bne at, t1, !skipplayid
126 | nop
127 | lbu t2, $0188 (T7)
128 | lui t3, $80ff
129 | sb t2, $5fef (T3)
130 |
131 | !skipplayid:
132 | sw r0, $0134 (T7)
133 | lui t2, $80ff
134 | lbu t2, $5ff4 (T2)
135 | addiu at, r0, $0003
136 | bne t2, at, !asbfuiasbzufasfasbzuf
137 | nop
138 | !nonsolid:
139 | addiu at, r0, $ffff
140 | sw at, $009c (t7)
141 | lui at, $8000
142 | sw at, $0130 (T7)
143 | !asbfuiasbzufasfasbzuf:
144 | lwc1 f2, $002c (T7)
145 | lui at, $4214
146 | mtc1 at, f4
147 | mul.s f4, f4, f2
148 | swc1 f2, $01f8 (T7)
149 |
150 | lui at, $4320
151 | mtc1 at, f4
152 | mul.s f4, f4, f2
153 | swc1 f2, $01fc (T7)
154 |
155 |
156 | lw ra, $14 (SP)
157 | jr ra
158 | addiu sp, sp, $001c
--------------------------------------------------------------------------------
/src/asm/ff9d00.txt:
--------------------------------------------------------------------------------
1 | .org 0x000
2 | !main:
3 | lui t7, $8036
4 | lw t7, $1160 (T7)
5 | lw t1, $0134 (T7)
6 | ori at, r0, $a000
7 | bne at, t1, !skipplayid
8 | nop
9 | lbu t2, $0188 (T7)
10 | lui t3, $80ff
11 | sb t2, $5fef (T3)
12 | !skipplayid:
13 | jr ra
14 | nop
--------------------------------------------------------------------------------
/src/asm/ff9d80.txt:
--------------------------------------------------------------------------------
1 | .org 0x000
2 | !main:
3 | lui t0, $80ff
4 | lbu t1, $5ff4 (T0)
5 | addiu at, r0, $5
6 | bne at, t1, !skip
7 | nop
8 | lbu t1, $7718 (T0)
9 | lbu t2, $7700 (T0)
10 | bne t1, t2, !skip
11 | nop
12 | lui t1, $8036
13 | lw t1, $1158 (T1)
14 | lui at, $4080
15 | mtc1 at, f2
16 | lwc1 f4, $002c (T1)
17 | mul.s f4, f4, f2
18 | swc1 f4, $002c (T1)
19 | swc1 f4, $0034 (T1)
20 |
21 | lwc1 f6, $0030 (T1)
22 | mul.s f6, f6, f2
23 | swc1 f6, $0030 (T1)
24 | !skip:
25 | jr a3
26 | nop
--------------------------------------------------------------------------------
/src/declarations/discord-rich-presence.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'discord-rich-presence'
2 |
--------------------------------------------------------------------------------
/src/declarations/winprocess.ts:
--------------------------------------------------------------------------------
1 | interface WinProcess {
2 | Process: (processId: number) => Process
3 | }
4 | export interface Process {
5 | open: () => void
6 | readMemory: (offset: number, length: number) => Buffer | number
7 | writeMemory: (offset: number, buffer: Buffer) => void
8 | }
9 | export default WinProcess
10 |
--------------------------------------------------------------------------------
/src/main/index.ts:
--------------------------------------------------------------------------------
1 | import { app, BrowserWindow } from 'electron'
2 |
3 | import * as path from 'path'
4 | import * as fs from 'fs'
5 | import * as RPCClientInstance from 'discord-rich-presence'
6 |
7 | import { Connector } from './Connector'
8 | import { Emulator } from './Emulator'
9 | import { Connection } from './Connection'
10 | import { ElectronSaveData } from '../models/State.model'
11 |
12 | interface Global extends NodeJS.Global {
13 | save: {
14 | appSaveData?: ElectronSaveData,
15 | appSavePath?: string,
16 | }
17 | }
18 |
19 | export let connector: Connector
20 | export let emulator: Emulator | undefined
21 | export let connection: Connection | undefined
22 |
23 | export const createEmulator = (
24 | { processId, characterId }:
25 | { processId: number, characterId: number }
26 | ) => {
27 | try {
28 | emulator = new Emulator(processId, characterId)
29 | emulator.displayChatMessage('- Net64 connected -')
30 | } catch (err) {
31 | console.warn(err)
32 | }
33 | }
34 |
35 | export const deleteEmulator = () => {
36 | emulator = undefined
37 | }
38 |
39 | export const createConnection = (
40 | { domain, ip, port, username, characterId }:
41 | {
42 | domain: string | undefined, ip: string | undefined, port: number | undefined, username: string, characterId: number,
43 | }
44 | ) => {
45 | connection = new Connection({ domain, ip, port, username, characterId })
46 | }
47 |
48 | export const deleteConnection = () => {
49 | if (connection) connection.disconnect()
50 | connection = undefined
51 | }
52 |
53 | export const RPCClient = new RPCClientInstance('560060224321355811')
54 | RPCClient.on('error', (err: Error) => {
55 | if (process.env.NODE_ENV === 'development') {
56 | console.error(err)
57 | }
58 | })
59 |
60 | export let RPCState = {}
61 |
62 | export function updateRPC (update: Record, clean?: boolean): void {
63 | if (clean) { RPCState = {} }
64 | Object.assign(RPCState, update)
65 | RPCClient.updatePresence(RPCState)
66 | }
67 |
68 | (() => {
69 | const onReady = () => {
70 | const mainWindow = new BrowserWindow({
71 | width: process.env.NODE_ENV === 'development' ? 1400 : 670,
72 | height: 840,
73 | icon: path.join(__dirname, 'img/icon.png'),
74 | title: `Net64+ ${process.env.VERSION}`,
75 | webPreferences: {
76 | webSecurity: false,
77 | nodeIntegrationInWorker: true
78 | }
79 | })
80 | connector = new Connector(mainWindow)
81 | updateRPC({
82 | state: 'Ready',
83 | details: 'Ready',
84 | largeImageKey: 'net64',
85 | largeImageText: `Net64+ ${process.env.VERSION}`
86 | })
87 | mainWindow.loadURL(path.normalize(`file://${__dirname}/index.html`))
88 |
89 | if (process.env.NODE_ENV === 'development') {
90 | // eslint-disable-next-line @typescript-eslint/no-var-requires
91 | require('electron-debug')({
92 | showDevTools: true
93 | })
94 | mainWindow.webContents.openDevTools()
95 | }
96 | }
97 |
98 | app.on('ready', onReady)
99 |
100 | app.on('window-all-closed', () => {
101 | RPCClient.disconnect()
102 | app.quit()
103 | })
104 |
105 | app.on('activate', () => {
106 | onReady()
107 | })
108 |
109 | process.on('uncaughtException', (err: Error) => {
110 | const errorFolderPath = path.resolve(__dirname, 'error')
111 | const filePath = path.resolve(
112 | errorFolderPath,
113 | `./error_log_${new Date().toISOString().split('.')[0].replace(/:/g, '').replace(/-/g, '')}.log`
114 | )
115 | if (!fs.existsSync(errorFolderPath)) {
116 | fs.mkdirSync(errorFolderPath)
117 | }
118 | fs.writeFileSync(
119 | filePath,
120 | `\
121 | Here is a detailed error log of the unhandled exception that caused Net64+ to crash.\n
122 | Please report this error log on GitHub: https://github.com/tarnadas/net64plus/issues\n\n\
123 | Error name: ${err.name}\n\
124 | Error message: ${err.message}\n\
125 | StackTrace: ${err.stack}`
126 | )
127 | app.quit()
128 | })
129 | })()
130 |
--------------------------------------------------------------------------------
/src/models/Emulator.mock.ts:
--------------------------------------------------------------------------------
1 | import { FilteredEmulator } from './Emulator.model'
2 | import { Process } from '../declarations/winprocess'
3 |
4 | export const testEmulatorPid = -1337
5 |
6 | export const testEmulator: FilteredEmulator = {
7 | name: 'Test Emu',
8 | pid: testEmulatorPid,
9 | windowName: 'Test Super Mario 64 - Project64'
10 | }
11 |
12 | const MEMORY_SIZE = 0xFFFFFF
13 | const PLAYER_POS_X_OFFSET = 0xFF7706
14 | const PLAYER_POS_Y_OFFSET = 0xFF770A
15 | const PLAYER_ROTATION_OFFSET = 0xFF7708
16 | const PLAYER_COURSE_OFFSET = 0xFF770F
17 |
18 | export class TestProcess implements Process {
19 | private readonly memory = Buffer.alloc(MEMORY_SIZE)
20 |
21 | constructor () {
22 | this.memory.writeUInt32LE(0x3C1A8032, 0)
23 | this.memory.writeUInt32LE(0x275A7650, 4)
24 | this.memory.writeInt16LE(0x1000, PLAYER_POS_X_OFFSET)
25 | this.memory.writeInt16LE(-0x800, PLAYER_POS_Y_OFFSET)
26 | setInterval(this.updatePlayerLocation.bind(this), 500)
27 | }
28 |
29 | private updatePlayerLocation () {
30 | for (let offset = 0; offset <= 0x100; offset += 0x100) {
31 | this.memory.writeInt16LE(
32 | (this.memory.readInt16LE(PLAYER_POS_X_OFFSET + offset) - 1),
33 | PLAYER_POS_X_OFFSET + offset
34 | )
35 | this.memory.writeInt16LE(
36 | (this.memory.readInt16LE(PLAYER_POS_Y_OFFSET + offset) - 1),
37 | PLAYER_POS_Y_OFFSET + offset
38 | )
39 | this.memory.writeUInt16LE(
40 | ((this.memory.readUInt16LE(PLAYER_ROTATION_OFFSET + offset) + 0x80) % 0xFFFF),
41 | PLAYER_ROTATION_OFFSET + offset
42 | )
43 | this.memory.writeUInt8(4, PLAYER_COURSE_OFFSET + offset)
44 | }
45 | }
46 |
47 | public open () {}
48 |
49 | public readMemory (offset: number, length: number): number | Buffer {
50 | let buffer: Buffer
51 | if (offset + length > MEMORY_SIZE) {
52 | buffer = Buffer.alloc(length)
53 | } else {
54 | buffer = this.memory.slice(offset, offset + length)
55 | }
56 | return buffer
57 | }
58 |
59 | public writeMemory (offset: number, buffer: Buffer) {
60 | this.memory.fill(buffer, offset, offset + buffer.length)
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/models/Emulator.model.ts:
--------------------------------------------------------------------------------
1 | import { IPlayer } from '../../proto/ServerClientMessage'
2 |
3 | export interface FilteredEmulator {
4 | name: string
5 | pid: number
6 | windowName?: string
7 | }
8 |
9 | export interface Position {
10 | x: number
11 | y: number
12 | rotation: number
13 | course: number
14 | }
15 |
16 | export interface Player extends IPlayer {
17 | position?: Position
18 | }
19 |
20 | export const CHARACTER_IMAGES = [
21 | 'mario.png',
22 | 'luigi.png',
23 | 'yoshi.png',
24 | 'wario.png',
25 | 'peach.png',
26 | 'toad.png',
27 | 'waluigi.png',
28 | 'rosalina.png',
29 | 'sonic.png',
30 | 'knuckles.png',
31 | 'goomba.png',
32 | 'kirby.png'
33 | ]
34 |
--------------------------------------------------------------------------------
/src/models/Message.model.ts:
--------------------------------------------------------------------------------
1 | export enum MainMessage {
2 | WEBSOCKET_CLOSE = 'WEBSOCKET_CLOSE',
3 | EMULATOR_DISCONNECT = 'EMULATOR_DISCONNECT',
4 | EMULATOR_CONNECTED = 'EMULATOR_CONNECTED',
5 | UPDATE_EMULATORS = 'UPDATE_EMULATORS',
6 | SET_SERVER = 'SET_SERVER',
7 | SET_PLAYERS = 'SET_PLAYERS',
8 | SET_PLAYER = 'SET_PLAYER',
9 | SET_PLAYER_ID = 'SET_PLAYER_ID',
10 | UPDATE_PLAYER_POSITIONS = 'UPDATE_PLAYER_POSITIONS',
11 | GAME_MODE = 'GAME_MODE',
12 | SERVER_FULL = 'SERVER_FULL',
13 | WRONG_VERSION = 'WRONG_VERSION',
14 | AUTHENTICATION_ACCEPTED = 'AUTHENTICATION_ACCEPTED',
15 | AUTHENTICATION_DENIED = 'AUTHENTICATION_DENIED',
16 | CHAT_GLOBAL = 'CHAT_GLOBAL',
17 | CHAT_COMMAND = 'CHAT_COMMAND',
18 | SET_CONNECTION_ERROR = 'SET_CONNECTION_ERROR',
19 | SET_EMULATOR_ERROR = 'SET_EMULATOR_ERROR',
20 | CONSOLE_INFO = 'CONSOLE_INFO',
21 | SET_CHARACTER = 'SET_CHARACTER'
22 | }
23 |
24 | export enum RendererMessage {
25 | CREATE_CONNECTION = 'CREATE_CONNECTION',
26 | DISCONNECT = 'DISCONNECT',
27 | UPDATE_EMULATORS = 'UPDATE_EMULATORS',
28 | CREATE_EMULATOR_CONNECTION = 'CREATE_EMULATOR_CONNECTION',
29 | DISCONNECT_EMULATOR = 'DISCONNECT_EMULATOR',
30 | PLAYER_UPDATE = 'PLAYER_UPDATE',
31 | PASSWORD = 'PASSWORD',
32 | CHAT_GLOBAL = 'CHAT_GLOBAL',
33 | CHAT_COMMAND = 'CHAT_COMMAND',
34 | EMU_CHAT = 'EMU_CHAT',
35 | HOTKEYS_CHANGED = 'HOTKEYS_CHANGED',
36 | CHARACTER_CYCLING_ORDER_CHANGED = 'CHARACTER_CYCLING_ORDER_CHANGED',
37 | GAMEPAD_BUTTON_STATE_CHANGED = 'GAMEPAD_BUTTON_STATE_CHANGED'
38 | }
39 |
--------------------------------------------------------------------------------
/src/models/Release.model.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable camelcase */
2 | interface Asset {
3 | browser_download_url: string
4 | name: string
5 | }
6 |
7 | export interface Release {
8 | draft: boolean
9 | prerelease: boolean
10 | assets: Asset[]
11 | tag_name: string
12 | body: string
13 | }
14 |
--------------------------------------------------------------------------------
/src/models/Server.model.ts:
--------------------------------------------------------------------------------
1 | import { Player } from './Emulator.model'
2 | import { GameModeType } from '../../proto/ServerClientMessage'
3 |
4 | export interface Server {
5 | id?: string
6 | domain?: string
7 | ip: string
8 | port: number
9 | name?: string
10 | description?: string
11 | players?: Array
12 | countryCode?: string
13 | gameMode?: GameModeType
14 | version?: string
15 | passwordRequired?: boolean | null
16 | isDedicated?: boolean
17 | }
18 |
19 | export interface Course {
20 | short: string
21 | long: string
22 | icon: string
23 | }
24 |
--------------------------------------------------------------------------------
/src/models/State.model.ts:
--------------------------------------------------------------------------------
1 | import { RouterState as ReactRouterState } from 'react-router-redux'
2 |
3 | import { Server } from './Server.model'
4 | import { FilteredEmulator, Position } from './Emulator.model'
5 | import { ChildProcess } from 'child_process'
6 | import { HotkeyShortcut } from '../main/HotkeyManager'
7 |
8 | export interface ElectronServerSaveDataDraft {
9 | name: string
10 | description: string
11 | gamemode: number
12 | enableGamemodeVote: boolean
13 | passwordRequired: boolean
14 | password: string
15 | port: number
16 | enableWebHook: boolean
17 | }
18 | export type ElectronServerSaveData = Readonly
19 |
20 | export interface ElectronSaveDataDraft {
21 | apiKey: string
22 | username: string
23 | character: number
24 | emuChat: boolean
25 | globalHotkeysEnabled: boolean
26 | hotkeyBindings: { [shortcut in HotkeyShortcut]: string[] }
27 | characterCylingOrder: Array<{characterId: number, on: boolean}>
28 | gamepadId: string | undefined
29 | lastIp: string
30 | lastPort: number
31 | version: string
32 | serverOptions: ElectronServerSaveData
33 | }
34 | export type ElectronSaveData = Readonly
35 |
36 | export interface SaveStateDraft {
37 | appSaveData: T
38 | appSavePath: string
39 | }
40 | export type SaveState = Readonly>
41 |
42 | export type RouterStateDraft = ReactRouterState
43 | export type RouterState = Readonly
44 |
45 | export interface ConnectionStateDraft {
46 | server: Server | null
47 | playerId: number | null
48 | selfPos: Position
49 | cameraAngle: number
50 | authenticated: boolean
51 | authenticationThrottle: number
52 | hasToken: boolean
53 | error: string
54 | }
55 | export type ConnectionState = Readonly
56 |
57 | export interface EmulatorStateDraft {
58 | emulators: FilteredEmulator[]
59 | isConnectedToEmulator: boolean
60 | error: string
61 | }
62 |
63 | export type EmulatorState = Readonly
64 |
65 | export enum IoChannel {
66 | Out, Warn, Err
67 | }
68 |
69 | export interface ConsoleServerMessage {
70 | key: string
71 | message: string
72 | channel: IoChannel
73 | }
74 |
75 | export interface ServerStateDraft {
76 | process: ChildProcess | null
77 | exitCode: number | null
78 | server: Server | null
79 | messages: ConsoleServerMessage[]
80 | }
81 | export type ServerState = Readonly
82 |
83 | export interface ChatMessage {
84 | key: number
85 | time: string
86 | message: string
87 | username: string
88 | isTrusted: boolean
89 | }
90 |
91 | export interface ChatStateDraft {
92 | global: ChatMessage[]
93 | }
94 | export type ChatState = Readonly
95 |
96 | export interface SnackbarStateDraft {
97 | message: string | null
98 | }
99 | export type SnackbarState = Readonly
100 |
101 | export interface StateDraft {
102 | save: SaveState
103 | router: RouterState
104 | connection: ConnectionState
105 | emulator: EmulatorState
106 | server: ServerState
107 | chat: ChatState
108 | snackbar: SnackbarState
109 | }
110 | export type State = Readonly
111 |
--------------------------------------------------------------------------------
/src/renderer/GamepadManager.ts:
--------------------------------------------------------------------------------
1 | import { Connector } from './Connector'
2 |
3 | export type ButtonState = Array<{
4 | /** The button identifier */
5 | key: number,
6 | /** The analog value of the button */
7 | value: number,
8 | /** Whether the button is pressed */
9 | pressed: boolean,
10 | }>
11 |
12 | export class GamepadManager {
13 | private readonly window: Window
14 | private readonly connector: Connector
15 | private _buttonState: ButtonState | undefined = undefined
16 | private readonly _buttonStateListeners: Array<(buttonState: ButtonState) => void> = []
17 |
18 | public selectedGamepad: Gamepad | undefined = undefined
19 |
20 | constructor (window: Window, connector: Connector, defaultGamepadId?: string) {
21 | this.window = window
22 | this.connector = connector
23 | this.window.addEventListener('gamepadconnected', (event) => {
24 | // If the default gamepad id is connected, and no other gamepad is selected, automatically bind
25 | const gamepadEvent = event as GamepadEvent
26 | if (this.selectedGamepad === undefined && gamepadEvent.gamepad.id === defaultGamepadId) {
27 | this.selectedGamepad = gamepadEvent.gamepad
28 | }
29 | })
30 |
31 | this.updateState = this.updateState.bind(this)
32 | this.emitButtonState = this.emitButtonState.bind(this)
33 | this.getConnectedGamepads = this.getConnectedGamepads.bind(this)
34 |
35 | requestAnimationFrame(this.updateState)
36 | }
37 |
38 | updateState () {
39 | const selectedGamepad = this.selectedGamepad
40 | if (selectedGamepad) {
41 | const gamepad = this.getConnectedGamepads().find((gamepad) =>
42 | (gamepad ? gamepad.id : undefined) === selectedGamepad.id
43 | )
44 | if (gamepad) {
45 | // If this is the initializer, skip change detection
46 | if (this._buttonState === undefined) {
47 | this._buttonState = gamepad.buttons.map((gamepadButton, index) =>
48 | ({ key: index, pressed: gamepadButton.pressed, value: gamepadButton.value })
49 | )
50 | requestAnimationFrame(this.updateState)
51 | return
52 | }
53 |
54 | // Detect changes in button state since last poll
55 | const changes: ButtonState = []
56 | for (let index = 0; index < gamepad.buttons.length; index++) {
57 | const button = gamepad.buttons[index]
58 | const oldButton = this._buttonState[index]
59 | if (oldButton === undefined || oldButton.pressed !== button.pressed || oldButton.value !== button.value) {
60 | changes.push({ key: index, pressed: button.pressed, value: button.value })
61 | }
62 | }
63 | if (changes.length > 0) {
64 | this._buttonState = gamepad.buttons.map((gamepadButton, index) =>
65 | ({ key: index, pressed: gamepadButton.pressed, value: gamepadButton.value })
66 | )
67 | // Emit an event to renderer and main with the changes
68 | this.emitButtonState(changes)
69 | }
70 | }
71 | }
72 | requestAnimationFrame(this.updateState)
73 | }
74 |
75 | private emitButtonState (buttonState: ButtonState) {
76 | this.connector.emitButtonState({ buttonState })
77 | this._buttonStateListeners.forEach((callback) => callback(buttonState))
78 | }
79 |
80 | public addButtonStateListener (callback: ((buttonState: ButtonState) => void)) {
81 | this._buttonStateListeners.push(callback)
82 | }
83 |
84 | public removeButtonStateListener (callback: ((buttonState: ButtonState) => void)) {
85 | const index = this._buttonStateListeners.findIndex((value) => value === callback)
86 | if (index !== -1) {
87 | this._buttonStateListeners.splice(index, 1)
88 | }
89 | }
90 |
91 | public getConnectedGamepads () {
92 | return Array.from(this.window.navigator.getGamepads())
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/renderer/actions/chat.ts:
--------------------------------------------------------------------------------
1 | import { AddGlobalChatMessageAction, ClearGlobalChatMessagesAction } from './models/chat.model'
2 | import { ChatMessage } from '../../models/State.model'
3 |
4 | export function addGlobalChatMessage (chatMessage: ChatMessage): AddGlobalChatMessageAction {
5 | return {
6 | type: 'ADD_GLOBAL_CHAT_MESSAGE',
7 | chatMessage
8 | }
9 | }
10 |
11 | export function clearGlobalChatMessages (): ClearGlobalChatMessagesAction {
12 | return {
13 | type: 'CLEAR_GLOBAL_CHAT_MESSAGES'
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/renderer/actions/connection.ts:
--------------------------------------------------------------------------------
1 | import {
2 | SetServerAction,
3 | SetConnectionErrorAction,
4 | SetPlayersAction,
5 | SetPlayerAction,
6 | SetPlayerIdAction,
7 | SetGameModeAction,
8 | AuthenticationRequired,
9 | AuthenticationAccepted,
10 | AuthenticationDenied,
11 | DisconnectAction,
12 | ConnectionActionType,
13 | UpdatePlayerPositionAction
14 | } from './models/connection.model'
15 | import { Server } from '../../models/Server.model'
16 | import { IPlayer, IPlayerUpdate } from '../../../proto/ServerClientMessage'
17 | import { Position } from '../../models/Emulator.model'
18 |
19 | export function setServer (server: Server): SetServerAction {
20 | return {
21 | type: ConnectionActionType.SET_SERVER,
22 | server
23 | }
24 | }
25 |
26 | export function setConnectionError (error: string): SetConnectionErrorAction {
27 | return {
28 | type: ConnectionActionType.SET_CONNECTION_ERROR,
29 | error
30 | }
31 | }
32 |
33 | export function setPlayers (players: IPlayerUpdate[]): SetPlayersAction {
34 | return {
35 | type: ConnectionActionType.SET_PLAYERS,
36 | players
37 | }
38 | }
39 |
40 | export function setPlayer (playerId: number, player: IPlayer): SetPlayerAction {
41 | return {
42 | type: ConnectionActionType.SET_PLAYER,
43 | playerId,
44 | player
45 | }
46 | }
47 |
48 | export function setPlayerId (playerId: number): SetPlayerIdAction {
49 | return {
50 | type: ConnectionActionType.SET_PLAYER_ID,
51 | playerId
52 | }
53 | }
54 |
55 | export function updatePlayerPositions (
56 | { self, cameraAngle, positions }: {self: Position, cameraAngle: number, positions: Array}
57 | ): UpdatePlayerPositionAction {
58 | return {
59 | type: ConnectionActionType.UPDATE_PLAYER_POSITIONS,
60 | self,
61 | cameraAngle,
62 | positions
63 | }
64 | }
65 |
66 | export function setGameMode (gameMode: number): SetGameModeAction {
67 | return {
68 | type: ConnectionActionType.GAME_MODE,
69 | gameMode
70 | }
71 | }
72 |
73 | export function authenticationRequired (): AuthenticationRequired {
74 | return {
75 | type: ConnectionActionType.AUTHENTICATION_REQUIRED
76 | }
77 | }
78 |
79 | export function authenticationAccepted (): AuthenticationAccepted {
80 | return {
81 | type: ConnectionActionType.AUTHENTICATION_ACCEPTED
82 | }
83 | }
84 |
85 | export function authenticationDenied (throttle: number): AuthenticationDenied {
86 | return {
87 | type: ConnectionActionType.AUTHENTICATION_DENIED,
88 | throttle
89 | }
90 | }
91 |
92 | export function disconnect (): DisconnectAction {
93 | return {
94 | type: ConnectionActionType.DISCONNECT
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/renderer/actions/emulator.ts:
--------------------------------------------------------------------------------
1 | import {
2 | SetEmulatorError, EmulatorActionType, IsConnectedToEmulatorAction, UpdateEmulatorsAction
3 | } from './models/emulator.model'
4 | import { FilteredEmulator } from '../../models/Emulator.model'
5 |
6 | export function updateEmulators (emulators: FilteredEmulator[]): UpdateEmulatorsAction {
7 | return {
8 | type: EmulatorActionType.UPDATE_EMULATORS,
9 | emulators
10 | }
11 | }
12 |
13 | export function isConnectedToEmulator (isConnectedToEmulator: boolean): IsConnectedToEmulatorAction {
14 | return {
15 | type: EmulatorActionType.IS_CONNECTED_TO_EMULATOR,
16 | isConnectedToEmulator
17 | }
18 | }
19 |
20 | export function setEmulatorError (error?: string): SetEmulatorError {
21 | return {
22 | type: EmulatorActionType.SET_ERROR,
23 | error
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/renderer/actions/models/chat.model.ts:
--------------------------------------------------------------------------------
1 | import { Action } from 'redux'
2 |
3 | import { ChatMessage } from '../../../models/State.model'
4 |
5 | export interface AddGlobalChatMessageAction extends Action {
6 | chatMessage: ChatMessage
7 | }
8 |
9 | export type ClearGlobalChatMessagesAction = Action
10 |
11 | export type ChatAction =
12 | AddGlobalChatMessageAction
13 | & ClearGlobalChatMessagesAction
14 |
--------------------------------------------------------------------------------
/src/renderer/actions/models/connection.model.ts:
--------------------------------------------------------------------------------
1 | import { Action } from 'redux'
2 |
3 | import { Server } from '../../../models/Server.model'
4 | import { IPlayer, IPlayerUpdate } from '../../../../proto/ServerClientMessage'
5 | import { Position } from '../../../models/Emulator.model'
6 |
7 | export interface SetServerAction extends Action {
8 | server: Server
9 | }
10 |
11 | export interface SetConnectionErrorAction extends Action {
12 | error: string
13 | }
14 |
15 | export interface SetPlayersAction extends Action {
16 | players: IPlayerUpdate[]
17 | }
18 |
19 | export interface SetPlayerAction extends Action {
20 | playerId: number
21 | player: IPlayer
22 | }
23 |
24 | export interface SetPlayerIdAction extends Action {
25 | playerId: number
26 | }
27 |
28 | export interface UpdatePlayerPositionAction extends Action {
29 | self: Position
30 | cameraAngle: number
31 | positions: Array
32 | }
33 |
34 | export interface SetGameModeAction extends Action {
35 | gameMode: number
36 | }
37 |
38 | export type AuthenticationRequired = Action
39 |
40 | export type AuthenticationAccepted = Action
41 |
42 | export interface AuthenticationDenied extends Action {
43 | throttle: number
44 | }
45 |
46 | export type DisconnectAction = Action
47 |
48 | export type ConnectionAction =
49 | SetServerAction
50 | & SetConnectionErrorAction
51 | & SetPlayersAction
52 | & SetPlayerAction
53 | & SetPlayerIdAction
54 | & UpdatePlayerPositionAction
55 | & SetGameModeAction
56 | & AuthenticationRequired
57 | & AuthenticationAccepted
58 | & AuthenticationDenied
59 | & DisconnectAction
60 |
61 | export enum ConnectionActionType {
62 | SET_SERVER = 'SET_SERVER',
63 | SET_CONNECTION_ERROR = 'SET_CONNECTION_ERROR',
64 | SET_PLAYERS = 'SET_PLAYERS',
65 | SET_PLAYER = 'SET_PLAYER',
66 | SET_PLAYER_ID = 'SET_PLAYER_ID',
67 | UPDATE_PLAYER_POSITIONS = 'UPDATE_PLAYER_POSITIONS',
68 | GAME_MODE = 'GAME_MODE',
69 | AUTHENTICATION_REQUIRED = 'AUTHENTICATION_REQUIRED',
70 | AUTHENTICATION_ACCEPTED = 'AUTHENTICATION_ACCEPTED',
71 | AUTHENTICATION_DENIED = 'AUTHENTICATION_DENIED',
72 | DISCONNECT = 'DISCONNECT'
73 | }
74 |
--------------------------------------------------------------------------------
/src/renderer/actions/models/emulator.model.ts:
--------------------------------------------------------------------------------
1 | import { Action } from 'redux'
2 |
3 | import { FilteredEmulator } from '../../../models/Emulator.model'
4 |
5 | export interface UpdateEmulatorsAction extends Action {
6 | emulators: FilteredEmulator[]
7 | }
8 |
9 | export interface IsConnectedToEmulatorAction extends Action {
10 | isConnectedToEmulator: boolean
11 | }
12 |
13 | export interface SetEmulatorError extends Action {
14 | error?: string
15 | }
16 |
17 | export type EmulatorAction =
18 | UpdateEmulatorsAction
19 | & IsConnectedToEmulatorAction
20 | & SetEmulatorError
21 |
22 | export enum EmulatorActionType {
23 | UPDATE_EMULATORS = 'UPDATE_EMULATORS',
24 | SET_ERROR = 'SET_EMULATOR_ERROR',
25 | IS_CONNECTED_TO_EMULATOR = 'IS_CONNECTED_TO_EMULATOR'
26 | }
27 |
--------------------------------------------------------------------------------
/src/renderer/actions/models/router.model.ts:
--------------------------------------------------------------------------------
1 | import { LocationChangeAction } from 'react-router-redux'
2 |
3 | export type RouterAction =
4 | LocationChangeAction
5 |
--------------------------------------------------------------------------------
/src/renderer/actions/models/save.model.ts:
--------------------------------------------------------------------------------
1 | import { Action } from 'redux'
2 |
3 | import { ElectronServerSaveData } from '../../../models/State.model'
4 | import { HotkeyShortcut } from '../../../main/HotkeyManager'
5 |
6 | export interface SetUsernameAction extends Action {
7 | username: string
8 | }
9 |
10 | export interface SetCharacterAction extends Action {
11 | character: number
12 | }
13 |
14 | export interface SetEmuChatAction extends Action {
15 | emuChat: boolean
16 | }
17 |
18 | export interface SetGlobalHotkeysEnabledAction extends Action {
19 | globalHotkeysEnabled: boolean
20 | }
21 |
22 | export interface SetHotkeyBindingsAction extends Action {
23 | hotkeyBindings: { [shortcut in HotkeyShortcut]: string[] }
24 | }
25 |
26 | export interface SetCharacterCyclingOrderAction extends Action {
27 | characterCyclingOrder: Array<{characterId: number, on: boolean}>
28 | }
29 |
30 | export interface SetGamepadIdAction extends Action {
31 | gamepadId: string | undefined
32 | }
33 |
34 | export interface AddApiKeyAction extends Action {
35 | apiKey: string
36 | }
37 |
38 | export interface SetVersionAction extends Action {
39 | version: string
40 | }
41 |
42 | export interface SetServerOptionsAction extends Action {
43 | serverOptions: ElectronServerSaveData
44 | apiKey: string
45 | }
46 |
47 | export type SaveAction =
48 | SetUsernameAction
49 | & SetCharacterAction
50 | & SetEmuChatAction
51 | & SetGlobalHotkeysEnabledAction
52 | & SetHotkeyBindingsAction
53 | & SetCharacterCyclingOrderAction
54 | & SetGamepadIdAction
55 | & AddApiKeyAction
56 | & SetVersionAction
57 | & SetServerOptionsAction
58 |
--------------------------------------------------------------------------------
/src/renderer/actions/models/server.model.ts:
--------------------------------------------------------------------------------
1 | import { Action } from 'redux'
2 |
3 | import { ChildProcess } from 'child_process'
4 |
5 | export interface SetServerProcessAction extends Action {
6 | process: ChildProcess
7 | }
8 |
9 | export type RemoveServerProcessAction = Action
10 |
11 | export interface ExitServerProcessAction extends Action {
12 | code: number
13 | }
14 |
15 | export interface AddServerMessageAction extends Action {
16 | message: string
17 | isStdErr: boolean
18 | }
19 |
20 | export type ServerAction =
21 | SetServerProcessAction
22 | & RemoveServerProcessAction
23 | & ExitServerProcessAction
24 | & AddServerMessageAction
25 |
26 | export enum ServerActionType {
27 | SET_SERVER_PROCESS = 'SET_SERVER_PROCESS',
28 | REMOVE_SERVER_PROCESS = 'REMOVE_SERVER_PROCESS',
29 | EXIT_SERVER_PROCESS = 'EXIT_SERVER_PROCESS',
30 | ADD_SERVER_MESSAGE = 'ADD_SERVER_MESSAGE'
31 | }
32 |
--------------------------------------------------------------------------------
/src/renderer/actions/models/snackbar.model.ts:
--------------------------------------------------------------------------------
1 | import { Action } from 'redux'
2 |
3 | export interface ShowSnackbarAction extends Action {
4 | message: string
5 | }
6 |
7 | export type HideSnackbarAction = Action
8 |
9 | export type SnackbarAction =
10 | ShowSnackbarAction
11 | & HideSnackbarAction
12 |
13 | export enum SnackbarActionType {
14 | SHOW_SNACKBAR = 'SHOW_SNACKBAR',
15 | HIDE_SNACKBAR = 'HIDE_SNACKBAR'
16 | }
17 |
--------------------------------------------------------------------------------
/src/renderer/actions/save.ts:
--------------------------------------------------------------------------------
1 | import { Action } from 'redux'
2 |
3 | import {
4 | SetUsernameAction,
5 | SetCharacterAction,
6 | SetEmuChatAction,
7 | AddApiKeyAction,
8 | SetVersionAction,
9 | SetServerOptionsAction,
10 | SetGlobalHotkeysEnabledAction,
11 | SetGamepadIdAction
12 | } from './models/save.model'
13 | import { ElectronServerSaveData } from '../../models/State.model'
14 | import { HotkeyShortcut } from '../../main/HotkeyManager'
15 |
16 | export function setUsername (username: string): SetUsernameAction {
17 | return {
18 | type: 'SET_USERNAME',
19 | username
20 | }
21 | }
22 |
23 | export function setCharacter (character: number): SetCharacterAction {
24 | return {
25 | type: 'SET_CHARACTER',
26 | character
27 | }
28 | }
29 | export function setEmuChat (emuChat: boolean): SetEmuChatAction {
30 | return {
31 | type: 'SET_EMU_CHAT',
32 | emuChat
33 | }
34 | }
35 | export function setGlobalHotkeysEnabled (globalHotkeysEnabled: boolean): SetGlobalHotkeysEnabledAction {
36 | return {
37 | type: 'SET_GLOBAL_HOTKEYS',
38 | globalHotkeysEnabled
39 | }
40 | }
41 | export function setHotkeyBindings (hotkeyBindings: { [shortcut in HotkeyShortcut]: string[] }) {
42 | return {
43 | type: 'SET_HOTKEY_BINDINGS',
44 | hotkeyBindings
45 | }
46 | }
47 | export function setCharacterCyclingOrder (characterCyclingOrder: Array<{characterId: number, on: boolean}>) {
48 | return {
49 | type: 'SET_CHARACTER_CYCLING_ORDER',
50 | characterCyclingOrder
51 | }
52 | }
53 | export function setGamepadId (gamepadId: string | undefined): SetGamepadIdAction {
54 | return {
55 | type: 'SET_GAMEPAD_ID',
56 | gamepadId
57 | }
58 | }
59 |
60 | export function addApiKey (apiKey: string): AddApiKeyAction {
61 | return {
62 | type: 'ADD_API_KEY',
63 | apiKey
64 | }
65 | }
66 |
67 | export function deleteApiKey (): Action {
68 | return {
69 | type: 'DELETE_API_KEY'
70 | }
71 | }
72 |
73 | export function setVersion (version: string): SetVersionAction {
74 | return {
75 | type: 'SET_VERSION',
76 | version
77 | }
78 | }
79 |
80 | export function saveServerOptions (serverOptions: ElectronServerSaveData, apiKey: string): SetServerOptionsAction {
81 | return {
82 | type: 'SAVE_SERVER_OPTIONS',
83 | serverOptions,
84 | apiKey
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/renderer/actions/server.ts:
--------------------------------------------------------------------------------
1 | import { ChildProcess } from 'child_process'
2 |
3 | import {
4 | SetServerProcessAction,
5 | ServerActionType,
6 | RemoveServerProcessAction,
7 | AddServerMessageAction,
8 | ExitServerProcessAction
9 | } from './models/server.model'
10 |
11 | export function setServerProcess (process: ChildProcess): SetServerProcessAction {
12 | return {
13 | type: ServerActionType.SET_SERVER_PROCESS,
14 | process
15 | }
16 | }
17 |
18 | export function removeServerProcess (): RemoveServerProcessAction {
19 | return {
20 | type: ServerActionType.REMOVE_SERVER_PROCESS
21 | }
22 | }
23 |
24 | export function exitServerProcess (code: number): ExitServerProcessAction {
25 | return {
26 | type: ServerActionType.EXIT_SERVER_PROCESS,
27 | code
28 | }
29 | }
30 |
31 | export function addServerMessage (message: string, isStdErr = false): AddServerMessageAction {
32 | return {
33 | type: ServerActionType.ADD_SERVER_MESSAGE,
34 | message,
35 | isStdErr
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/renderer/actions/snackbar.ts:
--------------------------------------------------------------------------------
1 | import { ShowSnackbarAction, SnackbarActionType, HideSnackbarAction } from './models/snackbar.model'
2 |
3 | export function showSnackbar (message: string): ShowSnackbarAction {
4 | return {
5 | type: SnackbarActionType.SHOW_SNACKBAR,
6 | message
7 | }
8 | }
9 |
10 | export function hideSnackbar (): HideSnackbarAction {
11 | return {
12 | type: SnackbarActionType.HIDE_SNACKBAR
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/renderer/components/areas/ChatArea.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { connect } from 'react-redux'
3 |
4 | import { connector } from '../..'
5 | import { SMMButton } from '../buttons/SMMButton'
6 | import { ChatMessagePanel } from '../panels/ChatMessagePanel'
7 | import { State, ChatMessage } from '../../../models/State.model'
8 |
9 | interface ChatAreaProps {
10 | chat: ChatMessage[]
11 | }
12 |
13 | interface ChatAreaState {
14 | message: string
15 | }
16 |
17 | const MAX_LENGTH_CHAT_MESSAGE = 100
18 |
19 | class Area extends React.PureComponent {
20 | private chat: HTMLElement | null = null
21 |
22 | constructor (public props: ChatAreaProps) {
23 | super(props)
24 | this.state = {
25 | message: ''
26 | }
27 | this.onMessageChange = this.onMessageChange.bind(this)
28 | this.onKeyPress = this.onKeyPress.bind(this)
29 | this.onSend = this.onSend.bind(this)
30 | this.renderChatMessages = this.renderChatMessages.bind(this)
31 | }
32 |
33 | componentDidUpdate (prevProps: ChatAreaProps) {
34 | if (prevProps.chat === this.props.chat || !this.chat) return
35 | this.chat.scrollTop = this.chat.scrollHeight
36 | }
37 |
38 | onMessageChange (e: React.ChangeEvent) {
39 | let value = e.target.value
40 | if (value.length > MAX_LENGTH_CHAT_MESSAGE) {
41 | value = value.substr(0, MAX_LENGTH_CHAT_MESSAGE)
42 | }
43 | this.setState({
44 | message: value
45 | })
46 | }
47 |
48 | onKeyPress (e: React.KeyboardEvent) {
49 | if (e.key !== 'Enter') return
50 | if (!this.state.message) return
51 | this.sendChatMessage(this.state.message)
52 | }
53 |
54 | onSend () {
55 | if (!this.state.message) return
56 | this.sendChatMessage(this.state.message)
57 | }
58 |
59 | private sendChatMessage (message: string): void {
60 | if (message[0] === '/') {
61 | const [command, ...args] = message.substr(1).split(' ')
62 | connector.sendCommandMessage(command, args)
63 | } else {
64 | connector.sendGlobalChatMessage(this.state.message)
65 | }
66 | this.setState({
67 | message: ''
68 | })
69 | }
70 |
71 | renderChatMessages (chat: ChatMessage[]) {
72 | return chat.map(
73 | message =>
74 | message.isTrusted
75 | ? ', '