hytopia 0.1.14 → 0.1.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. package/README.md +5 -10
  2. package/bin/scripts.js +12 -23
  3. package/boilerplate/assets/certs/README.md +11 -6
  4. package/boilerplate/assets/certs/localhost.crt +20 -0
  5. package/boilerplate/assets/certs/localhost.key +27 -0
  6. package/docs/server.hytopia.md +1 -1
  7. package/docs/server.hytopia.port.md +2 -2
  8. package/docs/server.hytopia.supported_input_keys.md +1 -1
  9. package/docs/server.md +1 -1
  10. package/docs/server.port.md +2 -2
  11. package/docs/server.supported_input_keys.md +1 -1
  12. package/examples/character-controller/MyCharacterController.ts +230 -0
  13. package/examples/character-controller/README.md +15 -0
  14. package/examples/character-controller/assets/audio/sfx/damage.wav +0 -0
  15. package/examples/character-controller/assets/audio/sfx/step.wav +0 -0
  16. package/examples/character-controller/assets/certs/README.md +11 -0
  17. package/examples/character-controller/assets/certs/localhost.crt +20 -0
  18. package/examples/character-controller/assets/certs/localhost.key +27 -0
  19. package/examples/character-controller/assets/cubemaps/skybox/+x.png +0 -0
  20. package/examples/character-controller/assets/cubemaps/skybox/+y.png +0 -0
  21. package/examples/character-controller/assets/cubemaps/skybox/+z.png +0 -0
  22. package/examples/character-controller/assets/cubemaps/skybox/-x.png +0 -0
  23. package/examples/character-controller/assets/cubemaps/skybox/-y.png +0 -0
  24. package/examples/character-controller/assets/cubemaps/skybox/-z.png +0 -0
  25. package/examples/character-controller/assets/map.json +2623 -0
  26. package/examples/character-controller/assets/models/player.gltf +1 -0
  27. package/examples/character-controller/assets/textures/bricks.png +0 -0
  28. package/examples/character-controller/assets/textures/clay.png +0 -0
  29. package/examples/character-controller/assets/textures/diamond_ore.png +0 -0
  30. package/examples/character-controller/assets/textures/dirt.png +0 -0
  31. package/examples/character-controller/assets/textures/dragons_stone.png +0 -0
  32. package/examples/character-controller/assets/textures/glass.png +0 -0
  33. package/examples/character-controller/assets/textures/grass/+x.png +0 -0
  34. package/examples/character-controller/assets/textures/grass/+y.png +0 -0
  35. package/examples/character-controller/assets/textures/grass/+z.png +0 -0
  36. package/examples/character-controller/assets/textures/grass/-x.png +0 -0
  37. package/examples/character-controller/assets/textures/grass/-y.png +0 -0
  38. package/examples/character-controller/assets/textures/grass/-z.png +0 -0
  39. package/examples/character-controller/assets/textures/grass.png +0 -0
  40. package/examples/character-controller/assets/textures/gravel.png +0 -0
  41. package/examples/character-controller/assets/textures/ice.png +0 -0
  42. package/examples/character-controller/assets/textures/infected_shadowrock.png +0 -0
  43. package/examples/character-controller/assets/textures/log_side.png +0 -0
  44. package/examples/character-controller/assets/textures/log_top.png +0 -0
  45. package/examples/character-controller/assets/textures/mossy_coblestone.png +0 -0
  46. package/examples/character-controller/assets/textures/nuit.png +0 -0
  47. package/examples/character-controller/assets/textures/oak_leaves.png +0 -0
  48. package/examples/character-controller/assets/textures/oak_planks.png +0 -0
  49. package/examples/character-controller/assets/textures/sand.png +0 -0
  50. package/examples/character-controller/assets/textures/shadowrock.png +0 -0
  51. package/examples/character-controller/assets/textures/stone.png +0 -0
  52. package/examples/character-controller/assets/textures/stone_bricks.png +0 -0
  53. package/examples/character-controller/assets/textures/void_sand.png +0 -0
  54. package/examples/character-controller/assets/textures/water_still.png +0 -0
  55. package/examples/character-controller/bun.lockb +0 -0
  56. package/examples/character-controller/index.ts +43 -0
  57. package/examples/character-controller/package.json +14 -0
  58. package/examples/character-controller/tsconfig.json +27 -0
  59. package/examples/entity-spawn/README.md +15 -0
  60. package/examples/entity-spawn/assets/certs/README.md +11 -0
  61. package/examples/entity-spawn/assets/certs/localhost.crt +20 -0
  62. package/examples/entity-spawn/assets/certs/localhost.key +27 -0
  63. package/examples/entity-spawn/assets/cubemaps/skybox/+x.png +0 -0
  64. package/examples/entity-spawn/assets/cubemaps/skybox/+y.png +0 -0
  65. package/examples/entity-spawn/assets/cubemaps/skybox/+z.png +0 -0
  66. package/examples/entity-spawn/assets/cubemaps/skybox/-x.png +0 -0
  67. package/examples/entity-spawn/assets/cubemaps/skybox/-y.png +0 -0
  68. package/examples/entity-spawn/assets/cubemaps/skybox/-z.png +0 -0
  69. package/examples/entity-spawn/assets/map.json +2623 -0
  70. package/examples/entity-spawn/assets/models/player.gltf +1 -0
  71. package/examples/entity-spawn/assets/models/spider.gltf +1 -0
  72. package/examples/entity-spawn/assets/textures/bricks.png +0 -0
  73. package/examples/entity-spawn/assets/textures/clay.png +0 -0
  74. package/examples/entity-spawn/assets/textures/diamond_ore.png +0 -0
  75. package/examples/entity-spawn/assets/textures/dirt.png +0 -0
  76. package/examples/entity-spawn/assets/textures/dragons_stone.png +0 -0
  77. package/examples/entity-spawn/assets/textures/glass.png +0 -0
  78. package/examples/entity-spawn/assets/textures/grass/+x.png +0 -0
  79. package/examples/entity-spawn/assets/textures/grass/+y.png +0 -0
  80. package/examples/entity-spawn/assets/textures/grass/+z.png +0 -0
  81. package/examples/entity-spawn/assets/textures/grass/-x.png +0 -0
  82. package/examples/entity-spawn/assets/textures/grass/-y.png +0 -0
  83. package/examples/entity-spawn/assets/textures/grass/-z.png +0 -0
  84. package/examples/entity-spawn/assets/textures/grass.png +0 -0
  85. package/examples/entity-spawn/assets/textures/gravel.png +0 -0
  86. package/examples/entity-spawn/assets/textures/ice.png +0 -0
  87. package/examples/entity-spawn/assets/textures/infected_shadowrock.png +0 -0
  88. package/examples/entity-spawn/assets/textures/log_side.png +0 -0
  89. package/examples/entity-spawn/assets/textures/log_top.png +0 -0
  90. package/examples/entity-spawn/assets/textures/mossy_coblestone.png +0 -0
  91. package/examples/entity-spawn/assets/textures/nuit.png +0 -0
  92. package/examples/entity-spawn/assets/textures/oak_leaves.png +0 -0
  93. package/examples/entity-spawn/assets/textures/oak_planks.png +0 -0
  94. package/examples/entity-spawn/assets/textures/sand.png +0 -0
  95. package/examples/entity-spawn/assets/textures/shadowrock.png +0 -0
  96. package/examples/entity-spawn/assets/textures/stone.png +0 -0
  97. package/examples/entity-spawn/assets/textures/stone_bricks.png +0 -0
  98. package/examples/entity-spawn/assets/textures/void_sand.png +0 -0
  99. package/examples/entity-spawn/assets/textures/water_still.png +0 -0
  100. package/examples/entity-spawn/bun.lockb +0 -0
  101. package/examples/entity-spawn/index.ts +93 -0
  102. package/examples/entity-spawn/package.json +14 -0
  103. package/examples/entity-spawn/tsconfig.json +27 -0
  104. package/examples/payload-game/README.md +15 -0
  105. package/examples/payload-game/assets/certs/README.md +11 -0
  106. package/examples/payload-game/assets/certs/localhost.crt +20 -0
  107. package/examples/payload-game/assets/certs/localhost.key +27 -0
  108. package/examples/payload-game/bun.lockb +0 -0
  109. package/examples/payload-game/index.ts +12 -3
  110. package/examples/payload-game/package.json +14 -0
  111. package/examples/payload-game/tsconfig.json +27 -0
  112. package/package.json +2 -5
  113. package/server.api.json +6 -6
  114. package/server.d.ts +10 -3
  115. package/server.js +1 -1
  116. 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. If you're starting a new project, initialize it.
34
+ 2. Create a new project directory somewhere on your machine and navigate into it.
35
35
  ```bash
36
- bun init
36
+ mkdir my-project-directory && cd my-project-directory
37
37
  ```
38
38
 
39
- 3. Install the SDK in a new or existing project.
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
- 5. Start the server, use --watch for hot reloads as you make changes.
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
- 6. Visit https://play.hytopia.com - when prompted, enter `localhost:8080` - this is the hostname of the local server you started in the previous step.
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, mkcert generated certs will be stored here.
2
- We do this to allow https://localhost & wss://localhost support.
3
- Without this, play.hytopia.com requires a bunch of funky browser
4
- flag workarounds in order to connect to our local server.
5
- This is only used for local development and will be ignored
6
- when your game is deployed to HYTOPIA servers.
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-----
@@ -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 listen on. You can override this in your .env by setting PORT.
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 listen on. You can override this in your .env by setting PORT.
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 | number
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: string[]
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 listen on. You can override this in your .env by setting PORT.
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>
@@ -4,10 +4,10 @@
4
4
 
5
5
  ## PORT variable
6
6
 
7
- The port the server will listen on. You can override this in your .env by setting PORT.
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 | number
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: string[]
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.
@@ -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-----