hytopia 0.1.14 → 0.1.16
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +5 -10
- package/bin/scripts.js +12 -23
- package/boilerplate/assets/certs/README.md +11 -6
- package/boilerplate/assets/certs/localhost.crt +20 -0
- package/boilerplate/assets/certs/localhost.key +27 -0
- package/docs/server.hytopia.md +1 -1
- package/docs/server.hytopia.port.md +2 -2
- package/docs/server.hytopia.supported_input_keys.md +1 -1
- package/docs/server.md +1 -1
- package/docs/server.port.md +2 -2
- package/docs/server.supported_input_keys.md +1 -1
- package/examples/character-controller/MyCharacterController.ts +230 -0
- package/examples/character-controller/README.md +15 -0
- package/examples/character-controller/assets/audio/sfx/damage.wav +0 -0
- package/examples/character-controller/assets/audio/sfx/step.wav +0 -0
- package/examples/character-controller/assets/certs/README.md +11 -0
- package/examples/character-controller/assets/certs/localhost.crt +20 -0
- package/examples/character-controller/assets/certs/localhost.key +27 -0
- package/examples/character-controller/assets/cubemaps/skybox/+x.png +0 -0
- package/examples/character-controller/assets/cubemaps/skybox/+y.png +0 -0
- package/examples/character-controller/assets/cubemaps/skybox/+z.png +0 -0
- package/examples/character-controller/assets/cubemaps/skybox/-x.png +0 -0
- package/examples/character-controller/assets/cubemaps/skybox/-y.png +0 -0
- package/examples/character-controller/assets/cubemaps/skybox/-z.png +0 -0
- package/examples/character-controller/assets/map.json +2623 -0
- package/examples/character-controller/assets/models/player.gltf +1 -0
- package/examples/character-controller/assets/textures/bricks.png +0 -0
- package/examples/character-controller/assets/textures/clay.png +0 -0
- package/examples/character-controller/assets/textures/diamond_ore.png +0 -0
- package/examples/character-controller/assets/textures/dirt.png +0 -0
- package/examples/character-controller/assets/textures/dragons_stone.png +0 -0
- package/examples/character-controller/assets/textures/glass.png +0 -0
- package/examples/character-controller/assets/textures/grass/+x.png +0 -0
- package/examples/character-controller/assets/textures/grass/+y.png +0 -0
- package/examples/character-controller/assets/textures/grass/+z.png +0 -0
- package/examples/character-controller/assets/textures/grass/-x.png +0 -0
- package/examples/character-controller/assets/textures/grass/-y.png +0 -0
- package/examples/character-controller/assets/textures/grass/-z.png +0 -0
- package/examples/character-controller/assets/textures/grass.png +0 -0
- package/examples/character-controller/assets/textures/gravel.png +0 -0
- package/examples/character-controller/assets/textures/ice.png +0 -0
- package/examples/character-controller/assets/textures/infected_shadowrock.png +0 -0
- package/examples/character-controller/assets/textures/log_side.png +0 -0
- package/examples/character-controller/assets/textures/log_top.png +0 -0
- package/examples/character-controller/assets/textures/mossy_coblestone.png +0 -0
- package/examples/character-controller/assets/textures/nuit.png +0 -0
- package/examples/character-controller/assets/textures/oak_leaves.png +0 -0
- package/examples/character-controller/assets/textures/oak_planks.png +0 -0
- package/examples/character-controller/assets/textures/sand.png +0 -0
- package/examples/character-controller/assets/textures/shadowrock.png +0 -0
- package/examples/character-controller/assets/textures/stone.png +0 -0
- package/examples/character-controller/assets/textures/stone_bricks.png +0 -0
- package/examples/character-controller/assets/textures/void_sand.png +0 -0
- package/examples/character-controller/assets/textures/water_still.png +0 -0
- package/examples/character-controller/bun.lockb +0 -0
- package/examples/character-controller/index.ts +43 -0
- package/examples/character-controller/package.json +14 -0
- package/examples/character-controller/tsconfig.json +27 -0
- package/examples/entity-spawn/README.md +15 -0
- package/examples/entity-spawn/assets/certs/README.md +11 -0
- package/examples/entity-spawn/assets/certs/localhost.crt +20 -0
- package/examples/entity-spawn/assets/certs/localhost.key +27 -0
- package/examples/entity-spawn/assets/cubemaps/skybox/+x.png +0 -0
- package/examples/entity-spawn/assets/cubemaps/skybox/+y.png +0 -0
- package/examples/entity-spawn/assets/cubemaps/skybox/+z.png +0 -0
- package/examples/entity-spawn/assets/cubemaps/skybox/-x.png +0 -0
- package/examples/entity-spawn/assets/cubemaps/skybox/-y.png +0 -0
- package/examples/entity-spawn/assets/cubemaps/skybox/-z.png +0 -0
- package/examples/entity-spawn/assets/map.json +2623 -0
- package/examples/entity-spawn/assets/models/player.gltf +1 -0
- package/examples/entity-spawn/assets/models/spider.gltf +1 -0
- package/examples/entity-spawn/assets/textures/bricks.png +0 -0
- package/examples/entity-spawn/assets/textures/clay.png +0 -0
- package/examples/entity-spawn/assets/textures/diamond_ore.png +0 -0
- package/examples/entity-spawn/assets/textures/dirt.png +0 -0
- package/examples/entity-spawn/assets/textures/dragons_stone.png +0 -0
- package/examples/entity-spawn/assets/textures/glass.png +0 -0
- package/examples/entity-spawn/assets/textures/grass/+x.png +0 -0
- package/examples/entity-spawn/assets/textures/grass/+y.png +0 -0
- package/examples/entity-spawn/assets/textures/grass/+z.png +0 -0
- package/examples/entity-spawn/assets/textures/grass/-x.png +0 -0
- package/examples/entity-spawn/assets/textures/grass/-y.png +0 -0
- package/examples/entity-spawn/assets/textures/grass/-z.png +0 -0
- package/examples/entity-spawn/assets/textures/grass.png +0 -0
- package/examples/entity-spawn/assets/textures/gravel.png +0 -0
- package/examples/entity-spawn/assets/textures/ice.png +0 -0
- package/examples/entity-spawn/assets/textures/infected_shadowrock.png +0 -0
- package/examples/entity-spawn/assets/textures/log_side.png +0 -0
- package/examples/entity-spawn/assets/textures/log_top.png +0 -0
- package/examples/entity-spawn/assets/textures/mossy_coblestone.png +0 -0
- package/examples/entity-spawn/assets/textures/nuit.png +0 -0
- package/examples/entity-spawn/assets/textures/oak_leaves.png +0 -0
- package/examples/entity-spawn/assets/textures/oak_planks.png +0 -0
- package/examples/entity-spawn/assets/textures/sand.png +0 -0
- package/examples/entity-spawn/assets/textures/shadowrock.png +0 -0
- package/examples/entity-spawn/assets/textures/stone.png +0 -0
- package/examples/entity-spawn/assets/textures/stone_bricks.png +0 -0
- package/examples/entity-spawn/assets/textures/void_sand.png +0 -0
- package/examples/entity-spawn/assets/textures/water_still.png +0 -0
- package/examples/entity-spawn/bun.lockb +0 -0
- package/examples/entity-spawn/index.ts +93 -0
- package/examples/entity-spawn/package.json +14 -0
- package/examples/entity-spawn/tsconfig.json +27 -0
- package/examples/payload-game/README.md +15 -0
- package/examples/payload-game/assets/certs/README.md +11 -0
- package/examples/payload-game/assets/certs/localhost.crt +20 -0
- package/examples/payload-game/assets/certs/localhost.key +27 -0
- package/examples/payload-game/bun.lockb +0 -0
- package/examples/payload-game/index.ts +12 -3
- package/examples/payload-game/package.json +14 -0
- package/examples/payload-game/tsconfig.json +27 -0
- package/package.json +2 -5
- package/server.api.json +6 -6
- package/server.d.ts +10 -3
- package/server.js +1 -1
- package/tsconfig.json +1 -4
package/README.md
CHANGED
@@ -31,27 +31,22 @@ With these resources, you can quickly build and share immersive, voxel-style mul
|
|
31
31
|
|
32
32
|
1. Install a compatible JavaScript runtime. We recommend you use [Bun](https://bun.sh/), but [Node.js](https://nodejs.org/) and [Deno](https://deno.com/) can work with additional configuration. All examples will be given using Bun.
|
33
33
|
|
34
|
-
2.
|
34
|
+
2. Create a new project directory somewhere on your machine and navigate into it.
|
35
35
|
```bash
|
36
|
-
|
36
|
+
mkdir my-project-directory && cd my-project-directory
|
37
37
|
```
|
38
38
|
|
39
|
-
3.
|
40
|
-
```bash
|
41
|
-
bun add hytopia
|
42
|
-
```
|
43
|
-
|
44
|
-
4. Initialize boilerplate. Copies assets and an index.ts game script into your project.
|
39
|
+
3. Initialize a hytopia project. Sets up package.json and all dependencies, copies assets and an index.ts game script into your project.
|
45
40
|
```bash
|
46
41
|
bunx hytopia init
|
47
42
|
```
|
48
43
|
|
49
|
-
|
44
|
+
3. Start the server, use --watch for hot reloads as you make changes.
|
50
45
|
```bash
|
51
46
|
bun --watch index.ts
|
52
47
|
```
|
53
48
|
|
54
|
-
|
49
|
+
4. Visit https://play.hytopia.com - when prompted, enter `localhost:8080` - this is the hostname of the local server you started in the previous step.
|
55
50
|
|
56
51
|
Once you're up and running, here's some other resources to go further:
|
57
52
|
- [Game Examples](./examples)
|
package/bin/scripts.js
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
#!/usr/bin/env node
|
2
2
|
|
3
|
+
const { execSync } = require('child_process');
|
3
4
|
const fs = require('fs');
|
4
5
|
const path = require('path');
|
5
|
-
const mkcert = require('mkcert');
|
6
6
|
|
7
7
|
(async () => {
|
8
8
|
const command = process.argv[2];
|
@@ -10,35 +10,24 @@ const mkcert = require('mkcert');
|
|
10
10
|
/**
|
11
11
|
* Init command
|
12
12
|
*
|
13
|
-
* Initializes a new HYTOPIA project.
|
13
|
+
* Initializes a new HYTOPIA project. Accepting an optional
|
14
|
+
* project name as an argument.
|
15
|
+
*
|
16
|
+
* @example
|
17
|
+
* `bunx hytopia init my-project-name`
|
14
18
|
*/
|
15
19
|
if (command === 'init') {
|
16
20
|
const srcDir = path.join(__dirname, '..', 'boilerplate');
|
17
21
|
const destDir = process.cwd();
|
18
|
-
|
22
|
+
|
23
|
+
// Initialize project
|
24
|
+
console.log('🔧 Initializing project');
|
25
|
+
execSync('bun init --yes');
|
26
|
+
execSync('bun add hytopia');
|
27
|
+
|
19
28
|
// Copy boilerplate
|
20
29
|
console.log(`🖨️ Copying boilerplate to ${destDir}`);
|
21
30
|
fs.cpSync(srcDir, destDir, { recursive: true });
|
22
|
-
|
23
|
-
// Generate SSL cert and allow mkcert to auto handle CA/Cert setup
|
24
|
-
console.log(`🔒 Generating SSL cert for local development`);
|
25
|
-
const validity = 3650; // 10 years
|
26
|
-
const ca = await mkcert.createCA({
|
27
|
-
organization: 'HYTOPIA',
|
28
|
-
countryCode: 'US',
|
29
|
-
state: 'Washington',
|
30
|
-
locality: 'Seattle',
|
31
|
-
validity,
|
32
|
-
});
|
33
|
-
|
34
|
-
const cert = await mkcert.createCert({
|
35
|
-
ca: { key: ca.key, cert: ca.cert },
|
36
|
-
domains: ['localhost', '127.0.0.1'],
|
37
|
-
validity,
|
38
|
-
});
|
39
|
-
|
40
|
-
fs.writeFileSync(path.join(destDir, 'assets', 'certs', 'localhost.crt'), cert.cert);
|
41
|
-
fs.writeFileSync(path.join(destDir, 'assets', 'certs', 'localhost.key'), cert.key);
|
42
31
|
|
43
32
|
// Done, lfg!
|
44
33
|
console.log('🚀 Hytopia project initialized successfully!');
|
@@ -1,6 +1,11 @@
|
|
1
|
-
For local development
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
For local development self-signed certs are stored here.
|
2
|
+
The server when GameServer.instance.webServer.enableLocalSSL();
|
3
|
+
is used, will look for assets/certs/localhost.key and
|
4
|
+
assets/certs/localhost.cert and use those for SSL.
|
5
|
+
|
6
|
+
We use these self-signed certs to allow https://localhost
|
7
|
+
& wss://localhost support. Without this, play.hytopia.com
|
8
|
+
requires a bunch of funky browser flag workarounds in
|
9
|
+
order to connect to your local server. This is only used
|
10
|
+
for local development and will be ignored when your game
|
11
|
+
is deployed to HYTOPIA servers.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDRjCCAi6gAwIBAgIFOTEzNTIwDQYJKoZIhvcNAQELBQAwWDEQMA4GA1UEAxMH
|
3
|
+
SFlUT1BJQTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV
|
4
|
+
BAcTB1NlYXR0bGUxEDAOBgNVBAoTB0hZVE9QSUEwHhcNMjQxMTEzMjIyMjEwWhcN
|
5
|
+
MzQxMTExMjIyMjEwWjAUMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3
|
6
|
+
DQEBAQUAA4IBDwAwggEKAoIBAQCo2i9L13+4KxvR9dxBeE3Xrj5WtoGFBwbUZkeV
|
7
|
+
3B8n+ehJ2LWD13sGvCuHvSDovbSOxLxqbESQzWBRVrmfJMR1qe+o7kd/SMaLHosC
|
8
|
+
2BWASn1oV9rGLktvd2Xs6xRf75cejzrr/OrNoTpICp3j1Z0Kd+73TuYyVFPfCbqH
|
9
|
+
N6mwS+yCmG2/w3sXwbOGymNuYpaPoD8XVcIhO7GPZsCVqqSKP2xsGF+oP5+Isma+
|
10
|
+
y3usNUnFnVW5ckk6SWWdxP9kuOW2xXoFFwj43STaE4OvXbTPCG7jowUpDH1aMvFA
|
11
|
+
ceXUfuaE7H8QfZqoUNu8jCb8LiqTL+uL5kHbl2kQ7sQCN9gjAgMBAAGjWzBZMAwG
|
12
|
+
A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMB
|
13
|
+
BggrBgEFBQcDAjAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcN
|
14
|
+
AQELBQADggEBAHSINJsVuu11kmzx7/2yEiDSIQVzdzk/+hnx0iQt6rDy9ZNVdI8w
|
15
|
+
JOUYaPjxDFfZ5gaPqrlb0n248lBMdGrp6xmhbgXL8eWkXRDcR6kuYBQrQmFitg7G
|
16
|
+
KFFWnzCuQMLn5Mn6VA+sBf9n2LVaOQCci9jU0awRpaQTUNf894USjILo+PvbcUGU
|
17
|
+
XlMtqc1abkN4NehDdaXsxMwyPgUCPkN5X80s8hxx5NgzwtwDtoTYj/PBRBXBddA+
|
18
|
+
oN1rwkugWiTpIPU0wKb0ufLsq96I24UDQWd2jJGbD2W23Z0lwVz/Bq6uc4aOJn68
|
19
|
+
JbejJgFyF3/m6oFDxgqMnuV5q5olVZHAWL8=
|
20
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,27 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIEowIBAAKCAQEAqNovS9d/uCsb0fXcQXhN164+VraBhQcG1GZHldwfJ/noSdi1
|
3
|
+
g9d7Brwrh70g6L20jsS8amxEkM1gUVa5nyTEdanvqO5Hf0jGix6LAtgVgEp9aFfa
|
4
|
+
xi5Lb3dl7OsUX++XHo866/zqzaE6SAqd49WdCnfu907mMlRT3wm6hzepsEvsgpht
|
5
|
+
v8N7F8GzhspjbmKWj6A/F1XCITuxj2bAlaqkij9sbBhfqD+fiLJmvst7rDVJxZ1V
|
6
|
+
uXJJOkllncT/ZLjltsV6BRcI+N0k2hODr120zwhu46MFKQx9WjLxQHHl1H7mhOx/
|
7
|
+
EH2aqFDbvIwm/C4qky/ri+ZB25dpEO7EAjfYIwIDAQABAoIBAA6dvGRbRiICEUlu
|
8
|
+
d99u84YM/jZxW+rQ/eVa6S1uvX+vYU0rJiNAftTJaxc35uZerYeCPjEUKCdEr+a6
|
9
|
+
lBzTiPIKgMVlwuiguxcF0NCxNcNCL3Ld0AFBtqPyeO82NjBLxBAQI/CTlxuvriTY
|
10
|
+
TmyPbCWg1h2wRbrrk8sFw5C638Rz2evbBfsUcTICr6QQa51YXgxawtew3Xy3aR3G
|
11
|
+
PPuS7zlvjJYDApQs3+nakEj3DnM2nz5oJNS2YEsMKQRhmNrhen6Ose5G0jf6F6up
|
12
|
+
8HVpN3XW01D7qVT1Og6Y5dE2EM/l3isKZWXif6CcFKoOy87z8HRMIoZJS62WtKtu
|
13
|
+
liBmYxkCgYEA4VDPtFAGnChCTGLVwo7MKmH6/KU5Qj2fuufGNw8jcW3H0Ea52OFC
|
14
|
+
qOGA9pRJVXCap7Wa7XCJXENLYWpSfB7H7GnhFLwHw7jAVB52DLDCdUnRv5fP3o8K
|
15
|
+
xEseMJQaJm95eFVfTETcnjDixWtIzNbXULQbGjF3AY74aFjc1fhc/yUCgYEAv9jj
|
16
|
+
Zrc+UzR0oi00Ip8O5C5Uvu2W35M+j2XoV/xPkX+Hcr12U40Gnc/rnzVp2lGTwNZS
|
17
|
+
ImQcjLEd31qLOy90F9lcaKvOfgcx0JvGEp5SdzQQaS3RJqn0xWLh1O4NMsiVbLNi
|
18
|
+
Zi5qFv7pW/DO174xSa7cMVE8qp8YHAOZdpcqm6cCgYB9FL2ZnetteTQE78E3NsrE
|
19
|
+
pIwPATIeQdOQkOaK3k6Qw57vUrQNYE//LODJAl0Ln21buQd+1MCMhZctRzbvTK8z
|
20
|
+
4TtgpVmyH7g1eouSU/Y/4nEcaYyuQr55ZjImSbGSsMPAN7SlxHytHPpnGj3Z6VKy
|
21
|
+
rKl3CwrDxLjGRL6j0jLeOQKBgBiu3xMoIGy0Iw16o2qQ7GmPTHgEOZUqbLImem5X
|
22
|
+
mCUwM00f80lsq51CUx540NW2CTyWVpr2JnYzk5RZIfDLejRXUvxknny4kEA2ypU6
|
23
|
+
qYGMwU/HBVHkLAn5XvT2a9xM0mzZ558d+ahbw8qAgRxg7BZ+2PW/bf7F2WRBUk1f
|
24
|
+
xauhAoGBALEspoxQozwohGQnP7EMF0/0JoKNpdNv0b0qCVvNiMo0+N297lI2mFQp
|
25
|
+
6xYlW/1l9afLokklF/J2IsyBrTCZoY7SaEk/lMMrQSyra+y0z71ogZ8A4ny9fxsj
|
26
|
+
0dDYJZGllL+3E/MQfd7k+KnOM/+A+cPoAnci76+L3vdkUb2P8SJk
|
27
|
+
-----END RSA PRIVATE KEY-----
|
package/docs/server.hytopia.md
CHANGED
@@ -741,7 +741,7 @@ The default rigid body options when EntityOptions.rigidBodyOptions is not provid
|
|
741
741
|
|
742
742
|
</td><td>
|
743
743
|
|
744
|
-
The port the server will
|
744
|
+
The port the server will run on. You can override this in your .env by setting PORT. When deployed in production to HYTOPIA servers, any .env value will be ignored and 8080 will be used.
|
745
745
|
|
746
746
|
|
747
747
|
</td></tr>
|
@@ -4,10 +4,10 @@
|
|
4
4
|
|
5
5
|
## HYTOPIA.PORT variable
|
6
6
|
|
7
|
-
The port the server will
|
7
|
+
The port the server will run on. You can override this in your .env by setting PORT. When deployed in production to HYTOPIA servers, any .env value will be ignored and 8080 will be used.
|
8
8
|
|
9
9
|
**Signature:**
|
10
10
|
|
11
11
|
```typescript
|
12
|
-
PORT: string |
|
12
|
+
PORT: string | 8080
|
13
13
|
```
|
@@ -9,5 +9,5 @@ The input keys included in the PlayerInputState.
|
|
9
9
|
**Signature:**
|
10
10
|
|
11
11
|
```typescript
|
12
|
-
SUPPORTED_INPUT_KEYS:
|
12
|
+
SUPPORTED_INPUT_KEYS: readonly ["w", "a", "s", "d", "sp", "sh", "tb", "ml", "mr", "q", "e", "r", "f", "z", "x", "c", "v", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
|
13
13
|
```
|
package/docs/server.md
CHANGED
@@ -750,7 +750,7 @@ The default rigid body options when EntityOptions.rigidBodyOptions is not provid
|
|
750
750
|
|
751
751
|
</td><td>
|
752
752
|
|
753
|
-
The port the server will
|
753
|
+
The port the server will run on. You can override this in your .env by setting PORT. When deployed in production to HYTOPIA servers, any .env value will be ignored and 8080 will be used.
|
754
754
|
|
755
755
|
|
756
756
|
</td></tr>
|
package/docs/server.port.md
CHANGED
@@ -4,10 +4,10 @@
|
|
4
4
|
|
5
5
|
## PORT variable
|
6
6
|
|
7
|
-
The port the server will
|
7
|
+
The port the server will run on. You can override this in your .env by setting PORT. When deployed in production to HYTOPIA servers, any .env value will be ignored and 8080 will be used.
|
8
8
|
|
9
9
|
**Signature:**
|
10
10
|
|
11
11
|
```typescript
|
12
|
-
PORT: string |
|
12
|
+
PORT: string | 8080
|
13
13
|
```
|
@@ -9,5 +9,5 @@ The input keys included in the PlayerInputState.
|
|
9
9
|
**Signature:**
|
10
10
|
|
11
11
|
```typescript
|
12
|
-
SUPPORTED_INPUT_KEYS:
|
12
|
+
SUPPORTED_INPUT_KEYS: readonly ["w", "a", "s", "d", "sp", "sh", "tb", "ml", "mr", "q", "e", "r", "f", "z", "x", "c", "v", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
|
13
13
|
```
|
@@ -0,0 +1,230 @@
|
|
1
|
+
import {
|
2
|
+
Audio,
|
3
|
+
BaseCharacterController,
|
4
|
+
CollisionGroup,
|
5
|
+
Collider,
|
6
|
+
Entity,
|
7
|
+
ColliderShape,
|
8
|
+
BlockType,
|
9
|
+
} from 'hytopia';
|
10
|
+
|
11
|
+
import type {
|
12
|
+
PlayerInputState,
|
13
|
+
PlayerOrientationState,
|
14
|
+
Vector3,
|
15
|
+
} from 'hytopia';
|
16
|
+
|
17
|
+
export default class MyCharacterController extends BaseCharacterController {
|
18
|
+
public jumpVelocity: number = 10;
|
19
|
+
public runVelocity: number = 8;
|
20
|
+
public walkVelocity: number = 4;
|
21
|
+
|
22
|
+
private _stepAudio: Audio;
|
23
|
+
private _groundContactCount: number = 0;
|
24
|
+
private _platform: Entity | undefined;
|
25
|
+
|
26
|
+
public constructor(entity: Entity) {
|
27
|
+
super(entity);
|
28
|
+
|
29
|
+
// Setup any audio or dependencies in the constructor.
|
30
|
+
this._stepAudio = new Audio({
|
31
|
+
uri: 'audio/sfx/step.wav',
|
32
|
+
loop: true,
|
33
|
+
volume: 0.1,
|
34
|
+
attachedToEntity: this.entity,
|
35
|
+
});
|
36
|
+
|
37
|
+
this.entity.lockAllRotations(); // prevent physics from applying rotation to the entity.
|
38
|
+
}
|
39
|
+
|
40
|
+
/** Whether the entity is grounded. */
|
41
|
+
public get isGrounded(): boolean { return this._groundContactCount > 0; }
|
42
|
+
|
43
|
+
/** Whether the entity is on a platform, a platform is any entity with a kinematic rigid body. */
|
44
|
+
public get isOnPlatform(): boolean { return !!this._platform; }
|
45
|
+
|
46
|
+
/** The platform the entity is on, if any. */
|
47
|
+
public get platform(): Entity | undefined { return this._platform; }
|
48
|
+
|
49
|
+
/**
|
50
|
+
* Create the sensor colliders for the character controller.
|
51
|
+
*/
|
52
|
+
public createSensorColliders(): Collider[] {
|
53
|
+
if (!this.entity.isSpawned) {
|
54
|
+
throw new Error('CharacterController.createSensorColliders(): Entity is not spawned!');
|
55
|
+
}
|
56
|
+
|
57
|
+
const sensorColliders: Collider[] = [];
|
58
|
+
|
59
|
+
/**
|
60
|
+
* Our ground sensor detects when we're on the ground.
|
61
|
+
* It assumes a cylinder shape and is positioned manually
|
62
|
+
* relative to the default entity rigid body as defined
|
63
|
+
* by the DEFAULT_ENTITY_RIGID_BODY_OPTIONS constant of
|
64
|
+
* the hytopia package.
|
65
|
+
*/
|
66
|
+
sensorColliders.push(new Collider({
|
67
|
+
shape: ColliderShape.CYLINDER,
|
68
|
+
radius: 0.30,
|
69
|
+
halfHeight: 0.125,
|
70
|
+
collisionGroups: {
|
71
|
+
belongsTo: [ CollisionGroup.ENTITY_SENSOR ],
|
72
|
+
collidesWith: [ CollisionGroup.BLOCK, CollisionGroup.ENTITY ],
|
73
|
+
},
|
74
|
+
isSensor: true,
|
75
|
+
relativeTranslation: { x: 0, y: -0.75, z: 0 },
|
76
|
+
tag: 'groundSensor',
|
77
|
+
onCollision: (_other: BlockType | Entity, started: boolean) => {
|
78
|
+
// Ground contact
|
79
|
+
this._groundContactCount += started ? 1 : -1;
|
80
|
+
|
81
|
+
if (!this._groundContactCount) { // Trigger animations
|
82
|
+
this.entity.startModelOneshotAnimations([ 'jump_loop' ]);
|
83
|
+
} else {
|
84
|
+
this.entity.stopModelAnimations([ 'jump_loop' ]);
|
85
|
+
}
|
86
|
+
|
87
|
+
// Platform contact
|
88
|
+
if (!(_other instanceof Entity) || !_other.isKinematic) return;
|
89
|
+
|
90
|
+
if (started) {
|
91
|
+
this._platform = _other;
|
92
|
+
} else if (_other === this._platform && !started) {
|
93
|
+
this._platform = undefined;
|
94
|
+
}
|
95
|
+
},
|
96
|
+
}));
|
97
|
+
|
98
|
+
return sensorColliders;
|
99
|
+
}
|
100
|
+
|
101
|
+
/**
|
102
|
+
* Handles movement of the entity based on player's input
|
103
|
+
* each tick. tickPlayerMovement is called internally if the entity
|
104
|
+
* is of the PlayerEntity class.
|
105
|
+
*/
|
106
|
+
public tickPlayerMovement(inputState: PlayerInputState, orientationState: PlayerOrientationState, deltaTimeMs: number) {
|
107
|
+
if (!this.entity.isSpawned || !this.entity.world) return; // type guard.
|
108
|
+
|
109
|
+
super.tickPlayerMovement(inputState, orientationState, deltaTimeMs);
|
110
|
+
|
111
|
+
const { w, a, s, d, sp, sh, ml, mr } = inputState; // See PlayerInputState type for all possible inputs.
|
112
|
+
const { yaw } = orientationState; // Camera/perspectie orientation of player.
|
113
|
+
const currentVelocity = this.entity.getLinearVelocity();
|
114
|
+
const targetVelocities = { x: 0, y: 0, z: 0 };
|
115
|
+
const isRunning = sh;
|
116
|
+
|
117
|
+
// Handle movement animations if relevant.
|
118
|
+
if (w || a || s || d) {
|
119
|
+
if (isRunning) {
|
120
|
+
// We stop irrelevant animations to prevent blending issues.
|
121
|
+
this.entity.stopModelAnimations(Array.from(this.entity.modelLoopedAnimations).filter(v => v !== 'run'));
|
122
|
+
// If run is already playing, internally startModelLoopedAnimations will do nothing.
|
123
|
+
this.entity.startModelLoopedAnimations([ 'run' ]);
|
124
|
+
// Manually set a playback rate of our step audio to audibly sync to our walk speed.
|
125
|
+
this._stepAudio.setPlaybackRate(0.83);
|
126
|
+
} else {
|
127
|
+
this.entity.stopModelAnimations(Array.from(this.entity.modelLoopedAnimations).filter(v => v !== 'walk'));
|
128
|
+
this.entity.startModelLoopedAnimations([ 'walk' ]);
|
129
|
+
this._stepAudio.setPlaybackRate(0.5);
|
130
|
+
}
|
131
|
+
|
132
|
+
this._stepAudio.play(this.entity.world, !this._stepAudio.isPlaying);
|
133
|
+
} else {
|
134
|
+
this._stepAudio.pause();
|
135
|
+
this.entity.stopModelAnimations(Array.from(this.entity.modelLoopedAnimations).filter(v => v !== 'idle'));
|
136
|
+
this.entity.startModelLoopedAnimations([ 'idle' ]);
|
137
|
+
}
|
138
|
+
|
139
|
+
// Play a simple interact animation on left mouse click then clear the input.
|
140
|
+
if (ml) {
|
141
|
+
this.entity.startModelOneshotAnimations([ 'simple_interact' ]);
|
142
|
+
inputState['ml'] = false;
|
143
|
+
}
|
144
|
+
|
145
|
+
// Rocket the player up on mouse right click.
|
146
|
+
if (mr) {
|
147
|
+
targetVelocities.y = 20;
|
148
|
+
inputState['mr'] = false;
|
149
|
+
}
|
150
|
+
|
151
|
+
// Calculate target horizontal velocities (run/walk)
|
152
|
+
const velocity = isRunning ? this.runVelocity : this.walkVelocity;
|
153
|
+
|
154
|
+
if (w) {
|
155
|
+
targetVelocities.x -= velocity * Math.sin(yaw);
|
156
|
+
targetVelocities.z -= velocity * Math.cos(yaw);
|
157
|
+
}
|
158
|
+
|
159
|
+
if (s) {
|
160
|
+
targetVelocities.x += velocity * Math.sin(yaw);
|
161
|
+
targetVelocities.z += velocity * Math.cos(yaw);
|
162
|
+
}
|
163
|
+
|
164
|
+
if (a) {
|
165
|
+
targetVelocities.x -= velocity * Math.cos(yaw);
|
166
|
+
targetVelocities.z += velocity * Math.sin(yaw);
|
167
|
+
}
|
168
|
+
|
169
|
+
if (d) {
|
170
|
+
targetVelocities.x += velocity * Math.cos(yaw);
|
171
|
+
targetVelocities.z -= velocity * Math.sin(yaw);
|
172
|
+
}
|
173
|
+
|
174
|
+
// Normalize for diagonals
|
175
|
+
const length = Math.sqrt(targetVelocities.x * targetVelocities.x + targetVelocities.z * targetVelocities.z);
|
176
|
+
if (length > velocity) {
|
177
|
+
const factor = velocity / length;
|
178
|
+
targetVelocities.x *= factor;
|
179
|
+
targetVelocities.z *= factor;
|
180
|
+
}
|
181
|
+
|
182
|
+
// Calculate target vertical velocity (jump)
|
183
|
+
if (sp) {
|
184
|
+
if (this.isGrounded && currentVelocity.y <= 3) {
|
185
|
+
targetVelocities.y = this.jumpVelocity;
|
186
|
+
}
|
187
|
+
}
|
188
|
+
|
189
|
+
// Apply impulse relative to target velocities, taking platform velocity into account
|
190
|
+
const platformVelocity = this._platform ? this._platform.getLinearVelocity() : { x: 0, y: 0, z: 0 };
|
191
|
+
const deltaVelocities = {
|
192
|
+
x: targetVelocities.x - currentVelocity.x + platformVelocity.x,
|
193
|
+
y: targetVelocities.y + platformVelocity.y,
|
194
|
+
z: targetVelocities.z - currentVelocity.z + platformVelocity.z,
|
195
|
+
};
|
196
|
+
|
197
|
+
const hasExternalVelocity =
|
198
|
+
Math.abs(currentVelocity.x) > this.runVelocity ||
|
199
|
+
Math.abs(currentVelocity.y) > this.jumpVelocity ||
|
200
|
+
Math.abs(currentVelocity.z) > this.runVelocity;
|
201
|
+
|
202
|
+
if (!hasExternalVelocity) { // allow external velocities like impulses to resolve, otherwise our deltas will cancel them out.
|
203
|
+
if (Object.values(deltaVelocities).some(v => v !== 0)) {
|
204
|
+
const mass = this.entity.getMass();
|
205
|
+
|
206
|
+
this.entity.applyImpulse({ // multiply by mass for the impulse to result in applying the correct target velocity
|
207
|
+
x: deltaVelocities.x * mass,
|
208
|
+
y: deltaVelocities.y * mass,
|
209
|
+
z: deltaVelocities.z * mass,
|
210
|
+
});
|
211
|
+
}
|
212
|
+
}
|
213
|
+
|
214
|
+
// Apply rotation relative to camera orientation.
|
215
|
+
if (yaw !== undefined) {
|
216
|
+
const halfYaw = yaw / 2;
|
217
|
+
|
218
|
+
this.entity.setRotation({
|
219
|
+
x: 0,
|
220
|
+
y: Math.fround(Math.sin(halfYaw)),
|
221
|
+
z: 0,
|
222
|
+
w: Math.fround(Math.cos(halfYaw)),
|
223
|
+
});
|
224
|
+
}
|
225
|
+
}
|
226
|
+
|
227
|
+
public tickPathfindingMovement(_destination: Vector3, _deltaTimeMs: number) {
|
228
|
+
console.log('Non-player pathfinding not implemented!');
|
229
|
+
}
|
230
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# character-controller
|
2
|
+
|
3
|
+
To install dependencies:
|
4
|
+
|
5
|
+
```bash
|
6
|
+
bun install
|
7
|
+
```
|
8
|
+
|
9
|
+
To run:
|
10
|
+
|
11
|
+
```bash
|
12
|
+
bun run index.ts
|
13
|
+
```
|
14
|
+
|
15
|
+
This project was created using `bun init` in bun v1.1.26. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
|
Binary file
|
Binary file
|
@@ -0,0 +1,11 @@
|
|
1
|
+
For local development self-signed certs are stored here.
|
2
|
+
The server when GameServer.instance.webServer.enableLocalSSL();
|
3
|
+
is used, will look for assets/certs/localhost.key and
|
4
|
+
assets/certs/localhost.cert and use those for SSL.
|
5
|
+
|
6
|
+
We use these self-signed certs to allow https://localhost
|
7
|
+
& wss://localhost support. Without this, play.hytopia.com
|
8
|
+
requires a bunch of funky browser flag workarounds in
|
9
|
+
order to connect to your local server. This is only used
|
10
|
+
for local development and will be ignored when your game
|
11
|
+
is deployed to HYTOPIA servers.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDRjCCAi6gAwIBAgIFOTEzNTIwDQYJKoZIhvcNAQELBQAwWDEQMA4GA1UEAxMH
|
3
|
+
SFlUT1BJQTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV
|
4
|
+
BAcTB1NlYXR0bGUxEDAOBgNVBAoTB0hZVE9QSUEwHhcNMjQxMTEzMjIyMjEwWhcN
|
5
|
+
MzQxMTExMjIyMjEwWjAUMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3
|
6
|
+
DQEBAQUAA4IBDwAwggEKAoIBAQCo2i9L13+4KxvR9dxBeE3Xrj5WtoGFBwbUZkeV
|
7
|
+
3B8n+ehJ2LWD13sGvCuHvSDovbSOxLxqbESQzWBRVrmfJMR1qe+o7kd/SMaLHosC
|
8
|
+
2BWASn1oV9rGLktvd2Xs6xRf75cejzrr/OrNoTpICp3j1Z0Kd+73TuYyVFPfCbqH
|
9
|
+
N6mwS+yCmG2/w3sXwbOGymNuYpaPoD8XVcIhO7GPZsCVqqSKP2xsGF+oP5+Isma+
|
10
|
+
y3usNUnFnVW5ckk6SWWdxP9kuOW2xXoFFwj43STaE4OvXbTPCG7jowUpDH1aMvFA
|
11
|
+
ceXUfuaE7H8QfZqoUNu8jCb8LiqTL+uL5kHbl2kQ7sQCN9gjAgMBAAGjWzBZMAwG
|
12
|
+
A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMB
|
13
|
+
BggrBgEFBQcDAjAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcN
|
14
|
+
AQELBQADggEBAHSINJsVuu11kmzx7/2yEiDSIQVzdzk/+hnx0iQt6rDy9ZNVdI8w
|
15
|
+
JOUYaPjxDFfZ5gaPqrlb0n248lBMdGrp6xmhbgXL8eWkXRDcR6kuYBQrQmFitg7G
|
16
|
+
KFFWnzCuQMLn5Mn6VA+sBf9n2LVaOQCci9jU0awRpaQTUNf894USjILo+PvbcUGU
|
17
|
+
XlMtqc1abkN4NehDdaXsxMwyPgUCPkN5X80s8hxx5NgzwtwDtoTYj/PBRBXBddA+
|
18
|
+
oN1rwkugWiTpIPU0wKb0ufLsq96I24UDQWd2jJGbD2W23Z0lwVz/Bq6uc4aOJn68
|
19
|
+
JbejJgFyF3/m6oFDxgqMnuV5q5olVZHAWL8=
|
20
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,27 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIEowIBAAKCAQEAqNovS9d/uCsb0fXcQXhN164+VraBhQcG1GZHldwfJ/noSdi1
|
3
|
+
g9d7Brwrh70g6L20jsS8amxEkM1gUVa5nyTEdanvqO5Hf0jGix6LAtgVgEp9aFfa
|
4
|
+
xi5Lb3dl7OsUX++XHo866/zqzaE6SAqd49WdCnfu907mMlRT3wm6hzepsEvsgpht
|
5
|
+
v8N7F8GzhspjbmKWj6A/F1XCITuxj2bAlaqkij9sbBhfqD+fiLJmvst7rDVJxZ1V
|
6
|
+
uXJJOkllncT/ZLjltsV6BRcI+N0k2hODr120zwhu46MFKQx9WjLxQHHl1H7mhOx/
|
7
|
+
EH2aqFDbvIwm/C4qky/ri+ZB25dpEO7EAjfYIwIDAQABAoIBAA6dvGRbRiICEUlu
|
8
|
+
d99u84YM/jZxW+rQ/eVa6S1uvX+vYU0rJiNAftTJaxc35uZerYeCPjEUKCdEr+a6
|
9
|
+
lBzTiPIKgMVlwuiguxcF0NCxNcNCL3Ld0AFBtqPyeO82NjBLxBAQI/CTlxuvriTY
|
10
|
+
TmyPbCWg1h2wRbrrk8sFw5C638Rz2evbBfsUcTICr6QQa51YXgxawtew3Xy3aR3G
|
11
|
+
PPuS7zlvjJYDApQs3+nakEj3DnM2nz5oJNS2YEsMKQRhmNrhen6Ose5G0jf6F6up
|
12
|
+
8HVpN3XW01D7qVT1Og6Y5dE2EM/l3isKZWXif6CcFKoOy87z8HRMIoZJS62WtKtu
|
13
|
+
liBmYxkCgYEA4VDPtFAGnChCTGLVwo7MKmH6/KU5Qj2fuufGNw8jcW3H0Ea52OFC
|
14
|
+
qOGA9pRJVXCap7Wa7XCJXENLYWpSfB7H7GnhFLwHw7jAVB52DLDCdUnRv5fP3o8K
|
15
|
+
xEseMJQaJm95eFVfTETcnjDixWtIzNbXULQbGjF3AY74aFjc1fhc/yUCgYEAv9jj
|
16
|
+
Zrc+UzR0oi00Ip8O5C5Uvu2W35M+j2XoV/xPkX+Hcr12U40Gnc/rnzVp2lGTwNZS
|
17
|
+
ImQcjLEd31qLOy90F9lcaKvOfgcx0JvGEp5SdzQQaS3RJqn0xWLh1O4NMsiVbLNi
|
18
|
+
Zi5qFv7pW/DO174xSa7cMVE8qp8YHAOZdpcqm6cCgYB9FL2ZnetteTQE78E3NsrE
|
19
|
+
pIwPATIeQdOQkOaK3k6Qw57vUrQNYE//LODJAl0Ln21buQd+1MCMhZctRzbvTK8z
|
20
|
+
4TtgpVmyH7g1eouSU/Y/4nEcaYyuQr55ZjImSbGSsMPAN7SlxHytHPpnGj3Z6VKy
|
21
|
+
rKl3CwrDxLjGRL6j0jLeOQKBgBiu3xMoIGy0Iw16o2qQ7GmPTHgEOZUqbLImem5X
|
22
|
+
mCUwM00f80lsq51CUx540NW2CTyWVpr2JnYzk5RZIfDLejRXUvxknny4kEA2ypU6
|
23
|
+
qYGMwU/HBVHkLAn5XvT2a9xM0mzZ558d+ahbw8qAgRxg7BZ+2PW/bf7F2WRBUk1f
|
24
|
+
xauhAoGBALEspoxQozwohGQnP7EMF0/0JoKNpdNv0b0qCVvNiMo0+N297lI2mFQp
|
25
|
+
6xYlW/1l9afLokklF/J2IsyBrTCZoY7SaEk/lMMrQSyra+y0z71ogZ8A4ny9fxsj
|
26
|
+
0dDYJZGllL+3E/MQfd7k+KnOM/+A+cPoAnci76+L3vdkUb2P8SJk
|
27
|
+
-----END RSA PRIVATE KEY-----
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|