Skip to content
This repository has been archived by the owner on Mar 17, 2024. It is now read-only.

Commit

Permalink
feat: chunk workers, names & fixe
Browse files Browse the repository at this point in the history
  • Loading branch information
Sceat committed Dec 17, 2023
1 parent 731806e commit 3e42932
Show file tree
Hide file tree
Showing 17 changed files with 662 additions and 134 deletions.
4 changes: 0 additions & 4 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,6 @@
href="https://fonts.googleapis.com/css2?family=PT+Sans:wght@400;700&display=swap"
rel="stylesheet"
/>
<link
href="https://unpkg.com/[email protected]/css/boxicons.min.css"
rel="stylesheet"
/>

<title>AresRPG.world</title>
<meta
Expand Down
283 changes: 272 additions & 11 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@vueuse/core": "^10.7.0",
"alea": "^1.0.1",
"aresrpg-protocol": "git+https://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:[email protected]/aresrpg/aresrpg-protocol.git",
"boxicons": "^2.1.4",
"camera-controls": "^2.7.3",
"dat.gui": "^0.7.9",
"fast-merge-async-iterators": "^1.0.7",
Expand All @@ -38,6 +39,7 @@
"stats.js": "^0.17.0",
"three": "^0.159.0",
"three-mesh-bvh": "^0.6.8",
"troika-three-text": "^0.49.0",
"vue": "^3.3.11",
"vue-router": "^4.2.5",
"vuesax-alpha": "^0.2.0-beta.62",
Expand Down
5 changes: 2 additions & 3 deletions src/game.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,6 @@ async function create_context({ send_packet, connect_ws }) {

const renderer = new WebGLRenderer()

const Pool = await create_pools({ scene, world })

renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setClearColor(0x263238 / 2, 1)
Expand All @@ -226,6 +224,7 @@ async function create_context({ send_packet, connect_ws }) {
2000, // Far clipping plane
)

const Pool = await create_pools({ scene, world, camera })
const orthographic_camera = new OrthographicCamera()

camera.far = 2000
Expand Down Expand Up @@ -358,7 +357,7 @@ export default async function create_game({
const state = get_state()
const delta_seconds = game_delta / 1000

world.step()
if (state.game_state === 'GAME') world.step()

permanent_modules
.map(({ tick }) => tick)
Expand Down
17 changes: 17 additions & 0 deletions src/interface/menu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
nav
img.logo(:src="logo")
.version build {{ pkg.version }}
.infos(v-if="menu_type === 'PLAY'")
vs-alert(color="#3498DB" type="gradient")
| We moved away from Minecraft as it was too limited for our needs. We are now using our own game engine, which allows us to do much more. We are still in early development, but we are working hard to bring you the best experience possible. Stay tuned!
| #[b If you own an early access key, you can already connect to the game and follow the development.]
template(#title) AresRPG is now a standalone game!

.menu_play(v-if="menu_type === 'PLAY'")
img.logo(:src="text_logo")
.btns
Expand Down Expand Up @@ -172,6 +178,17 @@ a
font-size .8em
color #212121
.infos
position absolute
top 1em
left 50%
transform translateX(-50%)
b
display flex
padding-top 1em
text-decoration underline
.menu_characters
position absolute
background rgba(#212121, .5)
Expand Down
1 change: 1 addition & 0 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { BufferGeometry, Mesh } from 'three'
import Vuesax from 'vuesax-alpha'
import 'vuesax-alpha/dist/index.css'
import 'vuesax-alpha/theme-chalk/dark/css-vars.css'
import 'boxicons'
import { inject } from '@vercel/analytics'

// @ts-ignore
Expand Down
15 changes: 12 additions & 3 deletions src/modules/game_nature.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,15 +142,24 @@ export default function () {
day_time = time
})

function get_player_chunk_position() {
try {
const player = get_state()?.player
if (!player) return new Vector3()
return to_chunk_position(player.position())
} catch (error) {
console.error(error)
return new Vector3()
}
}

aiter(abortable(setInterval(day_time_step, null, { signal }))).forEach(
() => {
// Update day_time and calculate day_ratio
day_time = (day_time + day_time_step) % DAY_DURATION
const day_ratio = day_time / DAY_DURATION

const chunk_position = to_chunk_position(
get_state()?.player?.position() ?? new Vector3(),
)
const chunk_position = get_player_chunk_position()

const light_base_position = new Vector3(
chunk_position.x * CHUNK_SIZE,
Expand Down
127 changes: 73 additions & 54 deletions src/modules/game_world.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import {
Audio,
AudioListener,
AudioLoader,
BackSide,
Color,
Mesh,
MeshBasicMaterial,
MeshPhongMaterial,
Quaternion,
Vector3,
} from 'three'
Expand All @@ -15,20 +19,21 @@ import {
CHUNK_SIZE,
} from 'aresrpg-protocol/src/chunk.js'
import { aiter, iter } from 'iterator-helper'
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'

import main_theme from '../assets/sound/main_theme.mp3'
import { PLAYER_ID } from '../game.js'
import { compute_animation_state } from '../utils/animation.js'
import log from '../utils/logger.js'
import {
request_chunk_load,
request_low_detail_chunk_load,
request_low_detail_chunks_load,
} from '../utils/chunks'
import { abortable } from '../utils/iterator'
import { create_navmesh } from '../utils/navmesh'

const make_chunk_key = ({ x, z }) => `${x}:${z}`
const from_chunk_key = key => {
export const from_chunk_key = key => {
const [x, z] = key.split(':')
return { x: +x, z: +z }
}
Expand All @@ -38,6 +43,7 @@ const sound = new Audio(listener)
const audio_loader = new AudioLoader()

const MOVE_UPDATE_INTERVAL = 0.1
const MAX_TITLE_VIEW_DISTANCE = CHUNK_SIZE * 1.3

const audio_buffer = await audio_loader.loadAsync(main_theme)

Expand All @@ -50,7 +56,7 @@ export default function () {
/** @typedef {{ terrain: import("three").Mesh, collider: import("three").Mesh, enable_collisions: (x: boolean) => void}} chunk */
/** @type {Map<string, chunk>} chunk position to chunk */
const loaded_chunks = new Map()
const low_detail_loaded_chunks = new Map()
let low_detail_plane = null
const entities = new Map()

return {
Expand Down Expand Up @@ -200,22 +206,31 @@ export default function () {
)
loaded_chunks.clear()

low_detail_loaded_chunks.forEach(({ terrain, dispose }) => {
scene.remove(terrain)
dispose()
})

low_detail_loaded_chunks.clear()
if (low_detail_plane) {
scene.remove(low_detail_plane)
low_detail_plane.geometry.dispose()
low_detail_plane.material.dispose()
low_detail_plane = null
}

camera_controls.colliderMeshes = []
}

events.once('STATE_UPDATED', () => {
events.once('STATE_UPDATED', ({ selected_character_id, characters }) => {
sound.play()
const player = Pool.guard.get({ add_rigid_body: true })
const player = Pool.guard.get({
add_rigid_body: true,
fixed_title_aspect: true,
})

player.three_body.position.setScalar(0)

const selected_character = characters.find(
({ id }) => id === selected_character_id,
)

player.title.text = selected_character.name

dispatch('action/register_player', {
...player,
id: PLAYER_ID,
Expand All @@ -227,10 +242,13 @@ export default function () {
})
})

events.on('packet/entitySpawn', ({ id, position, type }) => {
if (type === 0) {
events.on('packet/entitySpawn', ({ id, position, type, name }) => {
if (entities.has(id)) return

if (type === 'PLAYER') {
const entity = Pool.guard.get()
entity.id = id
entity.title.text = name
entity.move(position)
entities.set(id, entity)
}
Expand All @@ -247,7 +265,21 @@ export default function () {
events.on('packet/entityMove', ({ id, position }) => {
const entity = entities.get(id)
const { x, y, z } = position
if (entity) entity.target_position = new Vector3(x, y, z)
const state = get_state()
if (entity) {
entity.target_position = new Vector3(x, y, z)
if (state.player) {
const position = state.player.position()
const distance = position.distanceTo(new Vector3(x, y, z))
if (distance > MAX_TITLE_VIEW_DISTANCE && entity.title.visible)
entity.title.visible = false
else if (
distance <= MAX_TITLE_VIEW_DISTANCE &&
!entity.title.visible
)
entity.title.visible = true
}
}
})

events.on('CLEAR_CHUNKS', () => {
Expand Down Expand Up @@ -329,7 +361,7 @@ export default function () {
settings,
world: { seed, biome },
} = get_state()
const chunks_with_collisions = square_array(current_chunk, 1).map(
const chunks_with_collisions = square_array(current_chunk, 2).map(
make_chunk_key,
)
const new_chunks = spiral_array(
Expand Down Expand Up @@ -426,51 +458,38 @@ export default function () {
settings.far_view_distance,
).map(make_chunk_key)

const chunks_to_load = new_chunks.filter(
key => !low_detail_loaded_chunks.has(key),
)
const geometries = []

// Load low-detail chunks
await Promise.all(
chunks_to_load.map(async key => {
const { x, z } = from_chunk_key(key)
// Calculate the Manhattan distance from the current chunk to the center
const distance_from_center =
Math.abs(current_chunk.x - x) + Math.abs(current_chunk.z - z)
// Calculate segments based on distance
const segments = Math.max(
2,
// 16 >> (distance_from_center - settings.view_distance - 1),
4,
)

const { terrain, dispose } =
await request_low_detail_chunk_load({
chunk_x: x,
chunk_z: z,
biome,
seed,
segments,
})

low_detail_loaded_chunks.set(key, {
terrain,
dispose,
})
const low_detail_plane_geometry =
await request_low_detail_chunks_load({
chunks: new_chunks,
biome,
seed,
})

if (low_detail_plane) {
scene.remove(low_detail_plane)
low_detail_plane.geometry.dispose()
low_detail_plane.material.dispose()
}

low_detail_plane = new Mesh(
low_detail_plane_geometry,
new MeshPhongMaterial({
vertexColors: true,
color: new Color(0.4, 0.4, 0.4), // Darken the base color
emissive: new Color(0, 0, 0), // No additional light from the material itself
specular: new Color(0, 0, 0), // Low specular highlights
shininess: 10, // Adjust shininess for the size of the specular highlight
side: BackSide,
}),
)

// Add new low-detail terrain to the scene
low_detail_loaded_chunks.forEach(({ terrain, dispose }, key) => {
if (new_chunks.includes(key)) scene.add(terrain)
else {
scene.remove(terrain)
dispose()
low_detail_loaded_chunks.delete(key)
}
})
scene.add(low_detail_plane)
} catch (error) {
console.error(error)
} finally {
events.emit('CHUNKS_LOADED')
}
},
)
Expand Down
15 changes: 12 additions & 3 deletions src/modules/player_movement.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ export default function ({ world }) {

let is_dancing = false

let chunks_loaded = false

return {
name: 'player_movements',
tick({ inputs, player }, { camera, send_packet, events }, delta) {
Expand All @@ -134,15 +136,18 @@ export default function ({ world }) {

if (player.target_position) {
player.move(player.target_position)
events.emit('CHANGE_CHUNK', to_chunk_position(player.target_position))

player.target_position = null

return
}

// Avoid falling to hell
// TODO: tp to nether if falling to hell
if (position.y <= -30) {
velocity.setScalar(0)
player.move(new Vector3(0, 100, 0))
player.move(new Vector3(position.x, 100, position.z))
return
}

Expand Down Expand Up @@ -207,8 +212,8 @@ export default function ({ world }) {
break
case jump_states.NONE:
default:
// if not jumping, apply normal gravity
velocity.y -= GRAVITY * delta
// if not jumping, apply normal gravity as long as chunks are there
if (chunks_loaded) velocity.y -= GRAVITY * delta
}

movement.addScaledVector(velocity, delta)
Expand Down Expand Up @@ -292,6 +297,10 @@ export default function ({ world }) {
},
new Vector3(),
)

events.on('CHUNKS_LOADED', () => {
if (!chunks_loaded) chunks_loaded = true
})
},
}
}
Loading

0 comments on commit 3e42932

Please sign in to comment.