Skip to content

Commit

Permalink
feat: loading the game in a React app
Browse files Browse the repository at this point in the history
  • Loading branch information
RSamaium committed Jan 7, 2024
1 parent afba659 commit 437917a
Show file tree
Hide file tree
Showing 25 changed files with 11,749 additions and 13 deletions.
21 changes: 21 additions & 0 deletions docs/guide/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,27 @@ The configuration and properties of RPGJS are defined using a JSON schema, which

Configuration properties related to the compiler.

- `modulesRoot`: (*string*) The root directory for modules. (since 4.3.0)

- `modules`: (*array*) List of modules to load.

Example:

```toml
modules = ["./main", "@rpgjs/gamepad"]
```

- `autostart`: (*boolean*) Finds the starting point selector and displays the game directly. (`true` by default). Since 4.3.0

If `false`, it can be loaded as follows:

```ts
import { RpgClientEngine, inject } from '@rpgjs/client';

const client = inject(RpgClientEngine)
await client.start()
```

- `compilerOptions`
- `alias`: (*object*) Aliases. Use aliases in Typescript imports
```toml
Expand Down
17 changes: 17 additions & 0 deletions packages/client/src/Gui/React.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,21 @@ export class ReactGui {
}
this._gui.next(array)
}
}

type onReadyCallback = (engine: RpgClientEngine) => void
type RpgGameProps = {
onReady?: onReadyCallback
}

export function RpgGame({
onReady,
}: RpgGameProps) {
useEffect(() => {
const engine = inject(RpgClientEngine)
engine.start().then(() => {
onReady?.(engine)
})
}, [])
return createElement('div', { id: 'rpg' })
}
8 changes: 6 additions & 2 deletions packages/compiler/src/build/client-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ export interface Config {
[key: string]: any
},
vite?: any
modulesRoot?: string
autostart?: boolean
}

export interface ClientBuildConfigOptions {
Expand Down Expand Up @@ -117,6 +119,8 @@ export async function clientBuildConfig(dirname: string, options: ClientBuildCon

const envs = loadEnv(mode, cwd())
config = replaceEnvVars(config, envs)
config.autostart = config.autostart ?? true
config.modulesRoot = config.modulesRoot ?? ''

let buildOptions = config.compilerOptions?.build || {}

Expand Down Expand Up @@ -187,7 +191,7 @@ export async function clientBuildConfig(dirname: string, options: ClientBuildCon
configTomlPlugin(options, config), // after flagTransform
(requireTransform as any)(),
worldTransformPlugin(isRpg ? undefined : serverUrl),
tsxXmlPlugin(),
tsxXmlPlugin(config),
...(options.plugins || [])
]

Expand Down Expand Up @@ -231,7 +235,7 @@ export async function clientBuildConfig(dirname: string, options: ClientBuildCon
else {
if (!isBuild) {
plugins.push(
mapUpdatePlugin(isRpg ? undefined : serverUrl)
mapUpdatePlugin(isRpg ? '' : serverUrl, config)
)
}
}
Expand Down
17 changes: 12 additions & 5 deletions packages/compiler/src/build/vite-plugin-config.toml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ export function loadSpriteSheet(directoryName: string, modulePath: string, optio
}
}

export function loadClientFiles(modulePath: string, options, config) {
export function loadClientFiles(modulePath: string, options, config: Config) {
const importSceneMapString = importString(modulePath, 'scene-map', 'sceneMap')
const importSpriteString = importString(modulePath, 'sprite')
const importEngine = importString(modulePath, 'client', 'engine')
Expand Down Expand Up @@ -367,6 +367,13 @@ export default function configTomlPlugin(options: ClientBuildConfigOptions = {},
modules = config.modules;
}

modules = modules.map((module) => {
if (module.startsWith('.')) {
return './' + path.join(config.modulesRoot as string, module)
}
return module
})

config.startMap = config.startMap || config.start?.map

if (config.inputs && options.server) {
Expand Down Expand Up @@ -446,7 +453,7 @@ export default function configTomlPlugin(options: ClientBuildConfigOptions = {},
if (id.endsWith(MODULE_NAME)) {
const modulesToImport = modules.reduce((acc, module) => {
const variableName = formatVariableName(module);
acc[variableName] = module;
acc[variableName] = module
return acc;
}, {} as Record<string, string>);

Expand All @@ -468,11 +475,11 @@ export default function configTomlPlugin(options: ClientBuildConfigOptions = {},
import globalConfig from './${GLOBAL_CONFIG_CLIENT}'
document.addEventListener('DOMContentLoaded', function(e) {
entryPoint(modules, {
window.RpgStandalone = entryPoint(modules, {
io,
globalConfig,
envs: ${envsString}
}).start()
})${config.autostart ? '.start()' : ''}
});
`;
return codeToTransform
Expand Down Expand Up @@ -504,7 +511,7 @@ export default function configTomlPlugin(options: ClientBuildConfigOptions = {},
globalConfigClient,
globalConfigServer,
envs: ${envsString}
}).start()
})${config.autostart ? '.start()' : ''}
})`
}
Expand Down
5 changes: 4 additions & 1 deletion packages/compiler/src/build/vite-plugin-map-update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { errorApi, info } from '../logs/warning.js';
import fs from 'fs-extra';
import xml2js from 'xml2js';
import axios from '../serve/api.js';
import { type Config } from './client-config.js';

export function mapUpdatePlugin(_serverUrl?: string): Plugin {
export function mapUpdatePlugin(_serverUrl: string, config: Config): Plugin {
return {
name: 'vite-plugin-map-update',
configureServer(server) {
Expand All @@ -14,6 +15,8 @@ export function mapUpdatePlugin(_serverUrl?: string): Plugin {
server.watcher.add(globFiles('@(tmx|tsx)'));

server.watcher.on('change', async (file: string) => {
const gameRoot = (config.modulesRoot as string)
if (!file.startsWith(gameRoot)) return
if (file.endsWith('tmx')) {
info(`File ${file} changed, updating map...`)
// open file
Expand Down
8 changes: 5 additions & 3 deletions packages/compiler/src/build/vite-plugin-tsx-xml.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { Plugin } from 'vite';
import fs from 'fs';
import path from 'path';
import { type Config } from './client-config';

export const tsxXmlPlugin = (): Plugin => {
export const tsxXmlPlugin = (config: Config): Plugin => {
return {
name: 'tsx-xml-loader',
enforce: 'pre',

configureServer(server) {
server.middlewares.use((req, res, next) => {
if (req.url && (req.url.endsWith('.tsx')) && !req.url.includes('gui')) {
const { url } = req;
if (url && (url.startsWith('/' + config.modulesRoot as string)) && (url.endsWith('.tsx')) && !url.includes('gui')) {
const publicPath = server.config.root;
const filePath = path.join(publicPath, req.url);
const filePath = path.join(publicPath, url);
if (fs.existsSync(filePath)) {
const xmlContent = fs.readFileSync(filePath, 'utf-8');
res.setHeader('Content-Type', 'application/xml');
Expand Down
8 changes: 7 additions & 1 deletion packages/compiler/src/jsonSchema/compilation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,11 @@ export default {
"vite": {
"type": "object",
"additionalProperties": true
}
},
"modulesRoot": {
"type": "string"
},
"autostart": {
"type": "boolean"
},
}
1 change: 0 additions & 1 deletion packages/compiler/src/jsonSchema/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ export default {
}
}
},

"api": {
"type": "object",
"properties": {
Expand Down
18 changes: 18 additions & 0 deletions packages/sample3/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
24 changes: 24 additions & 0 deletions packages/sample3/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
30 changes: 30 additions & 0 deletions packages/sample3/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh

## Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:

- Configure the top-level `parserOptions` property like this:

```js
export default {
// other rules...
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: ['./tsconfig.json', './tsconfig.node.json'],
tsconfigRootDir: __dirname,
},
}
```

- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
53 changes: 53 additions & 0 deletions packages/sample3/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<!doctype html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>

<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<style>
body,
html {
height: 100%;
overflow: hidden;
}

body {
margin: 0;
background-color: black;
display: flex;
align-items: center;
justify-content: center;
}

#rpg {
position: relative;

}

@media (min-width: 800px) {
#rpg {
width: 1000px;
height: 800px;
}
}

@media (max-width: 800px) {
#rpg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
}
</style>
</body>

</html>
Loading

0 comments on commit 437917a

Please sign in to comment.