@dcl-regenesislabs/web-editor 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/editor.css +1 -0
- package/editor.js +33891 -0
- package/package.json +8 -4
- package/prefabs/admin-tools/composite.json +77 -0
- package/prefabs/admin-tools/data.json +28 -0
- package/prefabs/admin-tools/icons/admin-panel-control-button.png +0 -0
- package/prefabs/admin-tools/icons/admin-panel-moderation-control-button.png +0 -0
- package/prefabs/admin-tools/icons/admin-panel-rewards-control-button.png +0 -0
- package/prefabs/admin-tools/icons/admin-panel-smart-item-control-button.png +0 -0
- package/prefabs/admin-tools/icons/admin-panel-text-announcement-control-button.png +0 -0
- package/prefabs/admin-tools/icons/admin-panel-video-control-button.png +0 -0
- package/prefabs/admin-tools/icons/admin-tool-background.png +0 -0
- package/prefabs/admin-tools/icons/announcements/text-announcement-chat-message.png +0 -0
- package/prefabs/admin-tools/icons/announcements/text-announcement-check.png +0 -0
- package/prefabs/admin-tools/icons/announcements/text-announcement-close-button.png +0 -0
- package/prefabs/admin-tools/icons/moderation/admin-panel-verified-user.png +0 -0
- package/prefabs/admin-tools/icons/moderation/ban.png +0 -0
- package/prefabs/admin-tools/icons/moderation/chevron-back.png +0 -0
- package/prefabs/admin-tools/icons/moderation/chevron-forward.png +0 -0
- package/prefabs/admin-tools/icons/moderation/close.png +0 -0
- package/prefabs/admin-tools/icons/moderation/error.png +0 -0
- package/prefabs/admin-tools/icons/moderation/person-outline.png +0 -0
- package/prefabs/admin-tools/icons/moderation-control-icon.png +0 -0
- package/prefabs/admin-tools/icons/rewards/close.png +0 -0
- package/prefabs/admin-tools/icons/rewards/send.png +0 -0
- package/prefabs/admin-tools/icons/rewards-control.png +0 -0
- package/prefabs/admin-tools/icons/smart-item-control.png +0 -0
- package/prefabs/admin-tools/icons/text-announcement-control.png +0 -0
- package/prefabs/admin-tools/icons/video/chevron-down.png +0 -0
- package/prefabs/admin-tools/icons/video/chevron-up.png +0 -0
- package/prefabs/admin-tools/icons/video/copy-to-clipboard.png +0 -0
- package/prefabs/admin-tools/icons/video/error.png +0 -0
- package/prefabs/admin-tools/icons/video/eye-off.png +0 -0
- package/prefabs/admin-tools/icons/video/eye.png +0 -0
- package/prefabs/admin-tools/icons/video/help.png +0 -0
- package/prefabs/admin-tools/icons/video/info.png +0 -0
- package/prefabs/admin-tools/icons/video/person-outline.png +0 -0
- package/prefabs/admin-tools/icons/video/star.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-dcl-cast.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-live.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-loop.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-mute.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-pause-button.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-play-button.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-video-icon.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-volume-minus-button.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-volume-plus-button.png +0 -0
- package/prefabs/admin-tools/icons/video-control.png +0 -0
- package/prefabs/admin-tools/scripts/actions.ts +206 -0
- package/prefabs/admin-tools/scripts/admin.tsx +253 -0
- package/prefabs/admin-tools/scripts/api.ts +125 -0
- package/prefabs/admin-tools/scripts/components.ts +190 -0
- package/prefabs/admin-tools/scripts/icons.ts +53 -0
- package/prefabs/admin-tools/scripts/message-bus.ts +297 -0
- package/prefabs/admin-tools/scripts/state.ts +69 -0
- package/prefabs/admin-tools/scripts/tabs/Moderation.tsx +155 -0
- package/prefabs/admin-tools/scripts/tabs/Rewards.tsx +136 -0
- package/prefabs/admin-tools/scripts/tabs/SmartItemActions.tsx +167 -0
- package/prefabs/admin-tools/scripts/tabs/TextAnnouncements.tsx +195 -0
- package/prefabs/admin-tools/scripts/tabs/VideoControl.tsx +156 -0
- package/prefabs/admin-tools/scripts/tabs/announcements/Overlay.tsx +94 -0
- package/prefabs/admin-tools/scripts/tabs/announcements/icons.ts +15 -0
- package/prefabs/admin-tools/scripts/tabs/announcements/state.ts +71 -0
- package/prefabs/admin-tools/scripts/tabs/announcements/ui.tsx +37 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/AddUserInput.tsx +184 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/RemoveAdminConfirmation.tsx +93 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/UsersList.tsx +228 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/api.ts +35 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/icons.ts +23 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/kick.ts +95 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/state.ts +69 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/ui.tsx +172 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/utils.ts +88 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/Button.tsx +52 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/Captcha.tsx +88 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/api.ts +138 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/claim.ts +99 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/controller.ts +103 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/icons.ts +13 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/state.ts +32 -0
- package/prefabs/admin-tools/scripts/tabs/smart-items/selection.ts +90 -0
- package/prefabs/admin-tools/scripts/tabs/video/DclCast.tsx +226 -0
- package/prefabs/admin-tools/scripts/tabs/video/LiveStream.tsx +284 -0
- package/prefabs/admin-tools/scripts/tabs/video/SpeakerShowcase.tsx +112 -0
- package/prefabs/admin-tools/scripts/tabs/video/VideoUrl.tsx +107 -0
- package/prefabs/admin-tools/scripts/tabs/video/Volume.tsx +69 -0
- package/prefabs/admin-tools/scripts/tabs/video/api.ts +172 -0
- package/prefabs/admin-tools/scripts/tabs/video/controls.ts +108 -0
- package/prefabs/admin-tools/scripts/tabs/video/icons.ts +59 -0
- package/prefabs/admin-tools/scripts/tabs/video/speakers.ts +43 -0
- package/prefabs/admin-tools/scripts/tabs/video/state.ts +82 -0
- package/prefabs/admin-tools/scripts/tabs/video/ui.tsx +243 -0
- package/prefabs/admin-tools/scripts/types.ts +46 -0
- package/prefabs/admin-tools/scripts/ui.tsx +122 -0
- package/prefabs/admin-tools/thumbnail.png +0 -0
- package/prefabs/announcer/ai.md +76 -0
- package/prefabs/announcer/composite.json +31 -0
- package/prefabs/announcer/data.json +35 -0
- package/prefabs/announcer/scripts/announcer.tsx +79 -0
- package/prefabs/announcer/scripts/pure/toast.ts +30 -0
- package/prefabs/announcer/thumbnail.png +0 -0
- package/prefabs/black-chair/HighBackChair.glb +0 -0
- package/prefabs/black-chair/composite.json +74 -0
- package/prefabs/black-chair/data.json +30 -0
- package/prefabs/black-chair/scripts/seat.ts +191 -0
- package/prefabs/black-chair/thumbnail.png +0 -0
- package/prefabs/catalog.json +1 -0
- package/prefabs/classic-bench/Bench_01.glb +0 -0
- package/prefabs/classic-bench/composite.json +100 -0
- package/prefabs/classic-bench/data.json +34 -0
- package/prefabs/classic-bench/file1.png +0 -0
- package/prefabs/classic-bench/scripts/seat.ts +191 -0
- package/prefabs/classic-bench/thumbnail.png +0 -0
- package/prefabs/classic-bench-armrests/Bench 1.glb +0 -0
- package/prefabs/classic-bench-armrests/composite.json +100 -0
- package/prefabs/classic-bench-armrests/data.json +30 -0
- package/prefabs/classic-bench-armrests/scripts/seat.ts +191 -0
- package/prefabs/classic-bench-armrests/thumbnail.png +0 -0
- package/prefabs/classroom-chair/Chair_02.glb +0 -0
- package/prefabs/classroom-chair/composite.json +74 -0
- package/prefabs/classroom-chair/data.json +34 -0
- package/prefabs/classroom-chair/file1.png +0 -0
- package/prefabs/classroom-chair/scripts/seat.ts +191 -0
- package/prefabs/classroom-chair/thumbnail.png +0 -0
- package/prefabs/court-chair/Chair_Wood_01.glb +0 -0
- package/prefabs/court-chair/FanstasyPack_TX.png +0 -0
- package/prefabs/court-chair/composite.json +74 -0
- package/prefabs/court-chair/data.json +34 -0
- package/prefabs/court-chair/scripts/seat.ts +191 -0
- package/prefabs/court-chair/thumbnail.png +0 -0
- package/prefabs/curved-couch/CurvedCouch.glb +0 -0
- package/prefabs/curved-couch/composite.json +100 -0
- package/prefabs/curved-couch/data.json +30 -0
- package/prefabs/curved-couch/scripts/seat.ts +191 -0
- package/prefabs/curved-couch/thumbnail.png +0 -0
- package/prefabs/game-flow/ai.md +94 -0
- package/prefabs/game-flow/composite.json +45 -0
- package/prefabs/game-flow/data.json +27 -0
- package/prefabs/game-flow/scripts/game-flow.ts +168 -0
- package/prefabs/game-flow/scripts/pure/flow.ts +185 -0
- package/prefabs/game-flow/thumbnail.png +0 -0
- package/prefabs/health-respawn/ai.md +76 -0
- package/prefabs/health-respawn/composite.json +31 -0
- package/prefabs/health-respawn/data.json +38 -0
- package/prefabs/health-respawn/scripts/health-respawn.ts +98 -0
- package/prefabs/health-respawn/scripts/health.ts +22 -0
- package/prefabs/health-respawn/scripts/pure/health.ts +60 -0
- package/prefabs/health-respawn/thumbnail.png +0 -0
- package/prefabs/high-stool/HighStool.glb +0 -0
- package/prefabs/high-stool/composite.json +74 -0
- package/prefabs/high-stool/data.json +30 -0
- package/prefabs/high-stool/scripts/seat.ts +191 -0
- package/prefabs/high-stool/thumbnail.png +0 -0
- package/prefabs/large-couch/ThreeSeater_Couch.glb +0 -0
- package/prefabs/large-couch/composite.json +100 -0
- package/prefabs/large-couch/data.json +30 -0
- package/prefabs/large-couch/scripts/seat.ts +191 -0
- package/prefabs/large-couch/thumbnail.png +0 -0
- package/prefabs/leaderboard/ai.md +75 -0
- package/prefabs/leaderboard/board-panel.glb +0 -0
- package/prefabs/leaderboard/composite.json +82 -0
- package/prefabs/leaderboard/data.json +35 -0
- package/prefabs/leaderboard/scripts/leaderboard.ts +66 -0
- package/prefabs/leaderboard/scripts/pure/board.ts +71 -0
- package/prefabs/leaderboard/thumbnail.png +0 -0
- package/prefabs/loveseat/Couch_01.glb +0 -0
- package/prefabs/loveseat/composite.json +100 -0
- package/prefabs/loveseat/data.json +34 -0
- package/prefabs/loveseat/file1.png +0 -0
- package/prefabs/loveseat/scripts/seat.ts +191 -0
- package/prefabs/loveseat/thumbnail.png +0 -0
- package/prefabs/modern-sofa/ModernSofa.glb +0 -0
- package/prefabs/modern-sofa/composite.json +100 -0
- package/prefabs/modern-sofa/data.json +30 -0
- package/prefabs/modern-sofa/scripts/seat.ts +191 -0
- package/prefabs/modern-sofa/thumbnail.png +0 -0
- package/prefabs/outdoor-chair/Chair_07.glb +0 -0
- package/prefabs/outdoor-chair/composite.json +74 -0
- package/prefabs/outdoor-chair/data.json +34 -0
- package/prefabs/outdoor-chair/file1.png +0 -0
- package/prefabs/outdoor-chair/scripts/seat.ts +191 -0
- package/prefabs/outdoor-chair/thumbnail.png +0 -0
- package/prefabs/rustic-bench/Bench_01.glb +0 -0
- package/prefabs/rustic-bench/PiratesPack_TX.png.png +0 -0
- package/prefabs/rustic-bench/composite.json +100 -0
- package/prefabs/rustic-bench/data.json +34 -0
- package/prefabs/rustic-bench/scripts/seat.ts +191 -0
- package/prefabs/rustic-bench/thumbnail.png +0 -0
- package/prefabs/rustic-round-stool/Chairwood_01.glb +0 -0
- package/prefabs/rustic-round-stool/PiratesPack_TX.png.png +0 -0
- package/prefabs/rustic-round-stool/composite.json +74 -0
- package/prefabs/rustic-round-stool/data.json +34 -0
- package/prefabs/rustic-round-stool/scripts/seat.ts +191 -0
- package/prefabs/rustic-round-stool/thumbnail.png +0 -0
- package/prefabs/rustic-square-stool/Chairwood_02.glb +0 -0
- package/prefabs/rustic-square-stool/PiratesPack_TX.png.png +0 -0
- package/prefabs/rustic-square-stool/composite.json +74 -0
- package/prefabs/rustic-square-stool/data.json +34 -0
- package/prefabs/rustic-square-stool/scripts/seat.ts +191 -0
- package/prefabs/rustic-square-stool/thumbnail.png +0 -0
- package/prefabs/server-clock/ai.md +67 -0
- package/prefabs/server-clock/composite.json +45 -0
- package/prefabs/server-clock/data.json +24 -0
- package/prefabs/server-clock/scripts/clock-hud.tsx +55 -0
- package/prefabs/server-clock/scripts/server-clock.ts +71 -0
- package/prefabs/server-clock/thumbnail.png +0 -0
- package/prefabs/simple-chair/Chair_03.glb +0 -0
- package/prefabs/simple-chair/composite.json +74 -0
- package/prefabs/simple-chair/data.json +34 -0
- package/prefabs/simple-chair/file1.png +0 -0
- package/prefabs/simple-chair/scripts/seat.ts +191 -0
- package/prefabs/simple-chair/thumbnail.png +0 -0
- package/prefabs/sit-spot/composite.json +45 -0
- package/prefabs/sit-spot/data.json +30 -0
- package/prefabs/sit-spot/scripts/seat.ts +191 -0
- package/prefabs/sit-spot/thumbnail.png +0 -0
- package/prefabs/sit-spot-edge/composite.json +42 -0
- package/prefabs/sit-spot-edge/data.json +32 -0
- package/prefabs/sit-spot-edge/emotes/Sit_Edge1_emote.glb +0 -0
- package/prefabs/sit-spot-edge/emotes/Sit_Edge2_emote.glb +0 -0
- package/prefabs/sit-spot-edge/scripts/seat.ts +191 -0
- package/prefabs/sit-spot-edge/thumbnail.png +0 -0
- package/prefabs/small-couch/TwoSeater_Couch.glb +0 -0
- package/prefabs/small-couch/composite.json +100 -0
- package/prefabs/small-couch/data.json +30 -0
- package/prefabs/small-couch/scripts/seat.ts +191 -0
- package/prefabs/small-couch/thumbnail.png +0 -0
- package/prefabs/spawner/ai.md +109 -0
- package/prefabs/spawner/composite.json +73 -0
- package/prefabs/spawner/data.json +22 -0
- package/prefabs/spawner/scripts/spawner.ts +380 -0
- package/prefabs/spawner/thumbnail.png +0 -0
- package/prefabs/steampunk-bench/Steampunk Bench.glb +0 -0
- package/prefabs/steampunk-bench/composite.json +100 -0
- package/prefabs/steampunk-bench/data.json +30 -0
- package/prefabs/steampunk-bench/scripts/seat.ts +191 -0
- package/prefabs/steampunk-bench/thumbnail.png +0 -0
- package/prefabs/steampunk-chair/Steampunk Chair.glb +0 -0
- package/prefabs/steampunk-chair/composite.json +74 -0
- package/prefabs/steampunk-chair/data.json +30 -0
- package/prefabs/steampunk-chair/scripts/seat.ts +191 -0
- package/prefabs/steampunk-chair/thumbnail.png +0 -0
- package/prefabs/tall-stool/Stool_D.glb +0 -0
- package/prefabs/tall-stool/composite.json +74 -0
- package/prefabs/tall-stool/data.json +30 -0
- package/prefabs/tall-stool/scripts/seat.ts +191 -0
- package/prefabs/tall-stool/thumbnail.png +0 -0
- package/prefabs/trigger-zone/ai.md +115 -0
- package/prefabs/trigger-zone/composite.json +52 -0
- package/prefabs/trigger-zone/data.json +31 -0
- package/prefabs/trigger-zone/scripts/trigger-zone.ts +89 -0
- package/prefabs/trigger-zone/thumbnail.png +0 -0
- package/prefabs/video-screen/composite.json +98 -0
- package/prefabs/video-screen/data.json +21 -0
- package/prefabs/video-screen/thumbnail.png +0 -0
- package/prefabs/video-screen/video_player.glb +0 -0
- package/prefabs/wooden-chair/Chair 1.glb +0 -0
- package/prefabs/wooden-chair/composite.json +74 -0
- package/prefabs/wooden-chair/data.json +30 -0
- package/prefabs/wooden-chair/scripts/seat.ts +191 -0
- package/prefabs/wooden-chair/thumbnail.png +0 -0
- package/prefabs/wooden-chair-armrests/Chair 3.glb +0 -0
- package/prefabs/wooden-chair-armrests/composite.json +74 -0
- package/prefabs/wooden-chair-armrests/data.json +30 -0
- package/prefabs/wooden-chair-armrests/scripts/seat.ts +191 -0
- package/prefabs/wooden-chair-armrests/thumbnail.png +0 -0
- package/scene/about +17 -0
- package/scene/bafkreias2w4xuqaic6sdcvblrisayi6xg7drfowzpt5ubjkmrk3mx6czc4 +1 -0
- package/scene/bafkreiby2gmthtauf7cckttphmhpecsueyypi2hzlke4lcodg7fm4cm2sq +23 -0
- package/scene/bafkreidhwqwajqk4nsuffef5fkdrgmp5ql6vnn2knpqsuqalyosv5o6lk4 +1 -0
- package/scene/bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku +0 -0
- package/scene/bafybeihw3xlbff4sf5sc3kvmlmlwf2visnty5lfymawlunzt5vrkp5fd7u +90 -0
- package/web-build/esbuild.wasm +0 -0
- package/web-build/index.js +3 -0
- package/web-build/sdk-snapshot.json +1 -0
- package/web-build/worker.js +701 -0
- package/README.md +0 -3
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// Sit on this seat: click a sit spot -> the avatar is placed on it facing forward
|
|
2
|
+
// and a looping sitting emote plays; moving cancels the emote engine-side (the
|
|
3
|
+
// same mechanism the Creator Hub's seat smart items and Genesis Plaza use — no
|
|
4
|
+
// avatar attach). Spots are child entities named "Sit Spot…" carrying only a
|
|
5
|
+
// Transform; this script gives them their collider and pointer event at runtime.
|
|
6
|
+
// With no such children (the standalone Sit Spot prefab), the entity itself is
|
|
7
|
+
// the spot.
|
|
8
|
+
import {
|
|
9
|
+
ColliderLayer,
|
|
10
|
+
InputAction,
|
|
11
|
+
Material,
|
|
12
|
+
MeshCollider,
|
|
13
|
+
MeshRenderer,
|
|
14
|
+
Schemas,
|
|
15
|
+
Transform,
|
|
16
|
+
VisibilityComponent,
|
|
17
|
+
engine,
|
|
18
|
+
pointerEventsSystem,
|
|
19
|
+
type Entity,
|
|
20
|
+
type LastWriteWinElementSetComponentDefinition
|
|
21
|
+
} from '@dcl/sdk/ecs'
|
|
22
|
+
import { Quaternion, Vector3 } from '@dcl/sdk/math'
|
|
23
|
+
import { isServer } from '@dcl/sdk/network'
|
|
24
|
+
import { movePlayerTo, triggerEmote, triggerSceneEmote } from '~system/RestrictedActions'
|
|
25
|
+
|
|
26
|
+
const SPOT_NAME_PREFIX = 'sit spot'
|
|
27
|
+
const HOVER_TEXT = 'Sit Here'
|
|
28
|
+
const MAX_DISTANCE = 6
|
|
29
|
+
const EMOTE_DELAY_S = 0.25
|
|
30
|
+
const SITTING_EMOTES = ['sittingChair1', 'sittingChair2']
|
|
31
|
+
// emote .glb files bundled in this prefab's folder (Genesis Plaza-style custom
|
|
32
|
+
// poses, played via triggerSceneEmote); empty = the predefined chair emotes
|
|
33
|
+
const SCENE_EMOTES: string[] = []
|
|
34
|
+
|
|
35
|
+
// the Plaza-style "you can sit here" hint: a small glowing dot floating over
|
|
36
|
+
// each free spot, pulsing gently, shown only to nearby players
|
|
37
|
+
const DOT_VISIBLE_DIST = 10
|
|
38
|
+
const DOT_OCCUPIED_DIST = 0.9
|
|
39
|
+
const DOT_HEIGHT = 0.55
|
|
40
|
+
const DOT_SIZE = 0.09
|
|
41
|
+
const DOT_PULSE = 0.25
|
|
42
|
+
|
|
43
|
+
type NameValue = { value: string }
|
|
44
|
+
type NameComponent = LastWriteWinElementSetComponentDefinition<NameValue>
|
|
45
|
+
|
|
46
|
+
// Every placed seat prefab bundles its own copy of this script (prefab folders are
|
|
47
|
+
// self-contained), so the component may already be defined by a sibling copy.
|
|
48
|
+
function nameComponent(): NameComponent {
|
|
49
|
+
const existing = engine.getComponentOrNull('core-schema::Name')
|
|
50
|
+
if (existing !== null) return existing as NameComponent
|
|
51
|
+
return engine.defineComponent('core-schema::Name', { value: Schemas.String })
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function isSpotName(name: unknown): boolean {
|
|
55
|
+
return typeof name === 'string' && name.toLowerCase().startsWith(SPOT_NAME_PREFIX)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function sceneRelativeTransform(entity: Entity): { position: Vector3; rotation: Quaternion } {
|
|
59
|
+
const chain: Entity[] = []
|
|
60
|
+
let cursor = entity
|
|
61
|
+
while (cursor !== engine.RootEntity && chain.length < 32) {
|
|
62
|
+
chain.push(cursor)
|
|
63
|
+
const t = Transform.getOrNull(cursor)
|
|
64
|
+
if (t === null) break
|
|
65
|
+
cursor = t.parent ?? engine.RootEntity
|
|
66
|
+
}
|
|
67
|
+
let position = Vector3.Zero()
|
|
68
|
+
let rotation = Quaternion.Identity()
|
|
69
|
+
let scale = Vector3.One()
|
|
70
|
+
for (const link of chain.reverse()) {
|
|
71
|
+
const t = Transform.getOrNull(link)
|
|
72
|
+
if (t === null) continue
|
|
73
|
+
const local = Vector3.create(
|
|
74
|
+
t.position.x * scale.x,
|
|
75
|
+
t.position.y * scale.y,
|
|
76
|
+
t.position.z * scale.z
|
|
77
|
+
)
|
|
78
|
+
position = Vector3.add(position, Vector3.rotate(local, rotation))
|
|
79
|
+
rotation = Quaternion.multiply(rotation, t.rotation)
|
|
80
|
+
scale = Vector3.create(scale.x * t.scale.x, scale.y * t.scale.y, scale.z * t.scale.z)
|
|
81
|
+
}
|
|
82
|
+
return { position, rotation }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export class Seat {
|
|
86
|
+
private emoteIn = 0
|
|
87
|
+
private time = 0
|
|
88
|
+
private dots: Array<{ spot: Entity; dot: Entity; visible: boolean }> = []
|
|
89
|
+
|
|
90
|
+
constructor(
|
|
91
|
+
public src: string,
|
|
92
|
+
public entity: Entity
|
|
93
|
+
) {}
|
|
94
|
+
|
|
95
|
+
start(): void {
|
|
96
|
+
if (isServer()) { return }
|
|
97
|
+
const Name = nameComponent()
|
|
98
|
+
const spots: Entity[] = []
|
|
99
|
+
for (const [child, transform] of engine.getEntitiesWith(Transform)) {
|
|
100
|
+
if (transform.parent !== this.entity) continue
|
|
101
|
+
const name = Name.getOrNull(child)
|
|
102
|
+
if (name !== null && isSpotName(name.value)) spots.push(child)
|
|
103
|
+
}
|
|
104
|
+
if (spots.length === 0) spots.push(this.entity)
|
|
105
|
+
|
|
106
|
+
for (const spot of spots) {
|
|
107
|
+
MeshCollider.setBox(spot, ColliderLayer.CL_POINTER)
|
|
108
|
+
pointerEventsSystem.onPointerDown(
|
|
109
|
+
{
|
|
110
|
+
entity: spot,
|
|
111
|
+
opts: { button: InputAction.IA_POINTER, hoverText: HOVER_TEXT, maxDistance: MAX_DISTANCE }
|
|
112
|
+
},
|
|
113
|
+
() => this.sit(spot)
|
|
114
|
+
)
|
|
115
|
+
// no collider on the dot: clicks pass through to the spot beneath it
|
|
116
|
+
const dot = engine.addEntity()
|
|
117
|
+
Transform.create(dot, {
|
|
118
|
+
parent: spot,
|
|
119
|
+
position: Vector3.create(0, DOT_HEIGHT, 0),
|
|
120
|
+
scale: Vector3.create(DOT_SIZE, DOT_SIZE, DOT_SIZE)
|
|
121
|
+
})
|
|
122
|
+
MeshRenderer.setSphere(dot)
|
|
123
|
+
Material.setPbrMaterial(dot, {
|
|
124
|
+
albedoColor: { r: 1, g: 1, b: 1, a: 0.85 },
|
|
125
|
+
emissiveColor: { r: 1, g: 1, b: 1 },
|
|
126
|
+
emissiveIntensity: 2,
|
|
127
|
+
roughness: 1
|
|
128
|
+
})
|
|
129
|
+
VisibilityComponent.create(dot, { visible: false })
|
|
130
|
+
this.dots.push({ spot, dot, visible: false })
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
update(dt: number): void {
|
|
135
|
+
// The hint walks a transform chain per spot per frame, and a seat is placed
|
|
136
|
+
// by the dozen: without this the Multiplayer Server pays for every one.
|
|
137
|
+
if (isServer()) { return }
|
|
138
|
+
this.time += dt
|
|
139
|
+
this.updateDots()
|
|
140
|
+
if (this.emoteIn <= 0) return
|
|
141
|
+
this.emoteIn -= dt
|
|
142
|
+
if (this.emoteIn > 0) return
|
|
143
|
+
if (SCENE_EMOTES.length > 0) {
|
|
144
|
+
// src is the script's directory (custom/<slug>/scripts) — the emotes live
|
|
145
|
+
// one level up, in the placed prefab's own folder
|
|
146
|
+
const root = this.src.replace(/\/scripts\/?$/, '')
|
|
147
|
+
const emote = SCENE_EMOTES[Math.floor(Math.random() * SCENE_EMOTES.length)]
|
|
148
|
+
void triggerSceneEmote({ src: `${root}/${emote}`, loop: true })
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
const emote = SITTING_EMOTES[Math.floor(Math.random() * SITTING_EMOTES.length)]
|
|
152
|
+
void triggerEmote({ predefinedEmote: emote })
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private updateDots(): void {
|
|
156
|
+
const player = Transform.getOrNull(engine.PlayerEntity)?.position
|
|
157
|
+
const pulse = 1 + DOT_PULSE * Math.sin(this.time * 3)
|
|
158
|
+
for (const entry of this.dots) {
|
|
159
|
+
let visible = false
|
|
160
|
+
if (player !== undefined) {
|
|
161
|
+
const world = sceneRelativeTransform(entry.spot).position
|
|
162
|
+
const dist = Vector3.distance(player, world)
|
|
163
|
+
// hidden while someone (you) is on the spot — a glow over a seated
|
|
164
|
+
// avatar's head reads as a bug, not a hint
|
|
165
|
+
visible = dist < DOT_VISIBLE_DIST && dist > DOT_OCCUPIED_DIST
|
|
166
|
+
}
|
|
167
|
+
if (entry.visible !== visible) {
|
|
168
|
+
entry.visible = visible
|
|
169
|
+
VisibilityComponent.getMutable(entry.dot).visible = visible
|
|
170
|
+
}
|
|
171
|
+
if (visible) {
|
|
172
|
+
Transform.getMutable(entry.dot).scale = Vector3.create(
|
|
173
|
+
DOT_SIZE * pulse,
|
|
174
|
+
DOT_SIZE * pulse,
|
|
175
|
+
DOT_SIZE * pulse
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private sit(spot: Entity): void {
|
|
182
|
+
const world = sceneRelativeTransform(spot)
|
|
183
|
+
const forward = Vector3.rotate(Vector3.Forward(), world.rotation)
|
|
184
|
+
void movePlayerTo({
|
|
185
|
+
newRelativePosition: world.position,
|
|
186
|
+
avatarTarget: Vector3.add(world.position, forward)
|
|
187
|
+
})
|
|
188
|
+
// let the teleport settle before the emote starts, or the walk cancels it
|
|
189
|
+
this.emoteIn = EMOTE_DELAY_S
|
|
190
|
+
}
|
|
191
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
prefab: server-clock
|
|
3
|
+
claims-messages: runtime.timeSync, runtime.timeSyncResponse
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Server Clock — AI guide
|
|
7
|
+
|
|
8
|
+
One clock every player agrees on: the Multiplayer Server's time, NTP-sampled by
|
|
9
|
+
each client, so "the round ends at 12:04:30" means the same instant everywhere.
|
|
10
|
+
|
|
11
|
+
The project copy is normally custom/server_clock/ — check what is on disk, a
|
|
12
|
+
second copy is custom/server_clock_2/.
|
|
13
|
+
|
|
14
|
+
## When to use
|
|
15
|
+
|
|
16
|
+
Any shared deadline or shared elapsed time: a round timer, a countdown, a daily
|
|
17
|
+
event, a scoreboard's "started at". Also just to show the time. Needs an
|
|
18
|
+
scene with a Multiplayer Server (data.json requiresSdk: auth-server).
|
|
19
|
+
|
|
20
|
+
## API
|
|
21
|
+
|
|
22
|
+
import { getServerTime, initTimeSync, isTimeSyncReady } from './runtime/timeSync'
|
|
23
|
+
|
|
24
|
+
- getServerTime(): server-clock now, in epoch milliseconds. On the server it is
|
|
25
|
+
Date.now(); on a client it is Date.now() plus the measured offset.
|
|
26
|
+
- isTimeSyncReady(): false until the first samples land (a client takes a moment
|
|
27
|
+
after joining, and re-syncs every minute). Until then getServerTime() is just
|
|
28
|
+
local time — show a placeholder rather than a wrong time.
|
|
29
|
+
- initTimeSync(): starts the right half for the side it runs on. Idempotent, and
|
|
30
|
+
the placed prefab already calls it, so you only need it in a script that uses
|
|
31
|
+
the clock in a scene where the prefab might not be placed.
|
|
32
|
+
|
|
33
|
+
Params of the prefab's script — set them in the placePrefab request:
|
|
34
|
+
- label: text above the time (default 'SERVER TIME'; empty shows the time alone).
|
|
35
|
+
- utc: true (default) shows UTC, false shows each viewer's own timezone. A shared
|
|
36
|
+
deadline every player reads should stay UTC — local time differs per player.
|
|
37
|
+
- display: '3D text' (default) is floating text at the entity, '2D UI' is an
|
|
38
|
+
overlay on the player's client instead.
|
|
39
|
+
- position: where the 2D overlay sits — 'top' (default), 'top left', 'top right'
|
|
40
|
+
or 'bottom'. Ignored for '3D text'.
|
|
41
|
+
|
|
42
|
+
## Shared deadlines
|
|
43
|
+
|
|
44
|
+
Express a deadline as an absolute server timestamp, share THAT, and derive the
|
|
45
|
+
countdown locally every frame:
|
|
46
|
+
|
|
47
|
+
const endsAt = getServerTime() + 60_000
|
|
48
|
+
const secondsLeft = Math.max(0, Math.ceil((endsAt - getServerTime()) / 1000))
|
|
49
|
+
|
|
50
|
+
## Do / Don't
|
|
51
|
+
|
|
52
|
+
- DON'T build a countdown out of Date.now() or by accumulating dt: two players'
|
|
53
|
+
clocks differ by seconds and a reloading client loses its accumulator.
|
|
54
|
+
- DON'T re-implement time sync or add another timestamp message. Import this
|
|
55
|
+
module; the message names it registers are the scene's, and a second registrar
|
|
56
|
+
collides with it.
|
|
57
|
+
- DON'T format a time before isTimeSyncReady() — render '--:--:--' or hide it.
|
|
58
|
+
- The server decides WHEN, the client only renders it. Anything that must be the
|
|
59
|
+
same for everyone (round end, spawn moment) is decided on the server side of
|
|
60
|
+
the bundle, with isServer() from '@dcl/sdk/network'.
|
|
61
|
+
|
|
62
|
+
## Example
|
|
63
|
+
|
|
64
|
+
"Show a 60-second round timer everyone sees the same": place Server Clock (or
|
|
65
|
+
call initTimeSync() in a script of your own), store endsAt as a server timestamp
|
|
66
|
+
when the round starts, and render Math.ceil((endsAt - getServerTime()) / 1000)
|
|
67
|
+
in update() while isTimeSyncReady().
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"components": [
|
|
4
|
+
{
|
|
5
|
+
"name": "core-schema::Name",
|
|
6
|
+
"data": {
|
|
7
|
+
"0": {
|
|
8
|
+
"json": {
|
|
9
|
+
"value": "Server Clock"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "core::TextShape",
|
|
16
|
+
"data": {
|
|
17
|
+
"0": {
|
|
18
|
+
"json": {
|
|
19
|
+
"text": "SERVER TIME\n--:--:--",
|
|
20
|
+
"fontSize": 4,
|
|
21
|
+
"textColor": { "r": 1, "g": 1, "b": 1, "a": 1 },
|
|
22
|
+
"outlineColor": { "r": 0, "g": 0, "b": 0 },
|
|
23
|
+
"outlineWidth": 0.1
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "asset-packs::Script",
|
|
30
|
+
"data": {
|
|
31
|
+
"0": {
|
|
32
|
+
"json": {
|
|
33
|
+
"value": [
|
|
34
|
+
{
|
|
35
|
+
"path": "{assetPath}/scripts/server-clock.ts",
|
|
36
|
+
"priority": 0,
|
|
37
|
+
"layout": "{\"params\":{},\"actions\":[]}"
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "9e37c253-b33e-4505-8231-530f62715d21",
|
|
3
|
+
"name": "Server Clock",
|
|
4
|
+
"description": "A clock showing NTP-synced server time, identical for every player. Runs on the Multiplayer Server.",
|
|
5
|
+
"category": "custom",
|
|
6
|
+
"group": "Multiplayer Server",
|
|
7
|
+
"tags": [
|
|
8
|
+
"multiplayer",
|
|
9
|
+
"server",
|
|
10
|
+
"clock",
|
|
11
|
+
"time"
|
|
12
|
+
],
|
|
13
|
+
"requiresSdk": "auth-server",
|
|
14
|
+
"version": "1.0.0",
|
|
15
|
+
"changelog": [
|
|
16
|
+
{
|
|
17
|
+
"version": "1.0.0",
|
|
18
|
+
"notes": "Initial release: the Multiplayer Server's NTP-synced time, identical for every player, shown as 3D text at the item or as a 2D overlay on the player's client."
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"origin": {
|
|
22
|
+
"source": "builtin"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import ReactEcs, { Label, ReactEcsRenderer, UiEntity } from '@dcl/sdk/react-ecs'
|
|
2
|
+
import { type Entity } from '@dcl/sdk/ecs'
|
|
3
|
+
|
|
4
|
+
let label = ''
|
|
5
|
+
let time = ''
|
|
6
|
+
let mounted = false
|
|
7
|
+
|
|
8
|
+
export function setClockHudText(nextLabel: string, nextTime: string): void {
|
|
9
|
+
label = nextLabel
|
|
10
|
+
time = nextTime
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type ClockHudPosition = 'top' | 'top left' | 'top right' | 'bottom'
|
|
14
|
+
|
|
15
|
+
function justifyFor(where: ClockHudPosition): 'flex-start' | 'flex-end' | 'center' {
|
|
16
|
+
switch (where) {
|
|
17
|
+
case 'top left':
|
|
18
|
+
return 'flex-start'
|
|
19
|
+
case 'top right':
|
|
20
|
+
return 'flex-end'
|
|
21
|
+
default:
|
|
22
|
+
return 'center'
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function mountClockHud(entity: Entity, where: ClockHudPosition = 'top'): void {
|
|
27
|
+
if (mounted) return
|
|
28
|
+
mounted = true
|
|
29
|
+
const vertical = where === 'bottom' ? { bottom: 24, left: 0 } : { top: 24, left: 0 }
|
|
30
|
+
const justify = justifyFor(where)
|
|
31
|
+
ReactEcsRenderer.addUiRenderer(entity, () => (
|
|
32
|
+
<UiEntity
|
|
33
|
+
uiTransform={{
|
|
34
|
+
positionType: 'absolute',
|
|
35
|
+
position: vertical,
|
|
36
|
+
width: '100%',
|
|
37
|
+
padding: { left: 24, right: 24 },
|
|
38
|
+
justifyContent: justify,
|
|
39
|
+
alignItems: 'flex-start'
|
|
40
|
+
}}
|
|
41
|
+
>
|
|
42
|
+
<UiEntity
|
|
43
|
+
uiTransform={{
|
|
44
|
+
padding: { top: 6, bottom: 8, left: 18, right: 18 },
|
|
45
|
+
flexDirection: 'column',
|
|
46
|
+
alignItems: 'center'
|
|
47
|
+
}}
|
|
48
|
+
uiBackground={{ color: { r: 0.06, g: 0.09, b: 0.08, a: 0.6 } }}
|
|
49
|
+
>
|
|
50
|
+
{label !== '' ? <Label value={label} fontSize={12} color={{ r: 1, g: 1, b: 1, a: 0.75 }} /> : null}
|
|
51
|
+
<Label value={time === '' ? '--:--:--' : time} fontSize={26} color={{ r: 1, g: 1, b: 1, a: 1 }} />
|
|
52
|
+
</UiEntity>
|
|
53
|
+
</UiEntity>
|
|
54
|
+
))
|
|
55
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Entity, TextShape } from '@dcl/sdk/ecs'
|
|
2
|
+
import { isServer } from '@dcl/sdk/network'
|
|
3
|
+
import { initTimeSync, getServerTime, isTimeSyncReady } from '~runtime/timeSync'
|
|
4
|
+
import { mountClockHud, setClockHudText } from './clock-hud'
|
|
5
|
+
|
|
6
|
+
// Displays the Multiplayer Server's clock, NTP-synced, identical for every
|
|
7
|
+
// player — as floating 3D text, or as a 2D overlay on the player's client. Both
|
|
8
|
+
// halves start here: initTimeSync() registers the responder on the server and
|
|
9
|
+
// the asking half on the client, so placing the item is all the setup there is.
|
|
10
|
+
|
|
11
|
+
function pad(n: number): string {
|
|
12
|
+
return n < 10 ? `0${n}` : `${n}`
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class ServerClock {
|
|
16
|
+
private accum = 0
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
public src: string,
|
|
20
|
+
public entity: Entity,
|
|
21
|
+
/** Label above the time */ public label: string = 'SERVER TIME',
|
|
22
|
+
/** Show in UTC instead of each viewer's timezone */ public utc: boolean = true,
|
|
23
|
+
/** How the clock is shown */ public display: '3D text' | '2D UI' = '3D text',
|
|
24
|
+
/** Where the 2D UI sits in the player's view */ public position: 'top' | 'top left' | 'top right' | 'bottom' = 'top'
|
|
25
|
+
) {}
|
|
26
|
+
|
|
27
|
+
private get onScreen(): boolean {
|
|
28
|
+
return this.display === '2D UI'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
start(): void {
|
|
32
|
+
initTimeSync()
|
|
33
|
+
if (isServer()) return
|
|
34
|
+
if (this.onScreen) {
|
|
35
|
+
// the 3D text becomes the overlay — remove the placed shape
|
|
36
|
+
TextShape.deleteFrom(this.entity)
|
|
37
|
+
mountClockHud(this.entity, this.position)
|
|
38
|
+
}
|
|
39
|
+
this.render('--:--:--')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
update(dt: number): void {
|
|
43
|
+
if (isServer()) return
|
|
44
|
+
this.accum += dt
|
|
45
|
+
if (this.accum < 0.25) return
|
|
46
|
+
this.accum = 0
|
|
47
|
+
if (!isTimeSyncReady()) return
|
|
48
|
+
this.render(this.formatTime(new Date(getServerTime())))
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private formatTime(d: Date): string {
|
|
52
|
+
const parts = this.utc
|
|
53
|
+
? [d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds()]
|
|
54
|
+
: [d.getHours(), d.getMinutes(), d.getSeconds()]
|
|
55
|
+
return parts.map(pad).join(':')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private render(time: string): void {
|
|
59
|
+
if (this.onScreen) {
|
|
60
|
+
setClockHudText(this.label, time)
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
TextShape.createOrReplace(this.entity, {
|
|
64
|
+
text: this.label === '' ? time : `${this.label}\n${time}`,
|
|
65
|
+
fontSize: 4,
|
|
66
|
+
textColor: { r: 1, g: 1, b: 1, a: 1 },
|
|
67
|
+
outlineColor: { r: 0, g: 0, b: 0 },
|
|
68
|
+
outlineWidth: 0.1
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"components": [
|
|
4
|
+
{
|
|
5
|
+
"name": "core::GltfContainer",
|
|
6
|
+
"data": {
|
|
7
|
+
"0": {
|
|
8
|
+
"json": {
|
|
9
|
+
"src": "{assetPath}/Chair_03.glb",
|
|
10
|
+
"visibleMeshesCollisionMask": 0,
|
|
11
|
+
"invisibleMeshesCollisionMask": 3
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "core-schema::Name",
|
|
18
|
+
"data": {
|
|
19
|
+
"0": {
|
|
20
|
+
"json": {
|
|
21
|
+
"value": "Simple Chair"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"513": {
|
|
25
|
+
"json": {
|
|
26
|
+
"value": "Sit Spot"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"name": "core::Transform",
|
|
33
|
+
"data": {
|
|
34
|
+
"513": {
|
|
35
|
+
"json": {
|
|
36
|
+
"position": {
|
|
37
|
+
"x": -0.32,
|
|
38
|
+
"y": 0.15,
|
|
39
|
+
"z": 0
|
|
40
|
+
},
|
|
41
|
+
"rotation": {
|
|
42
|
+
"x": 0,
|
|
43
|
+
"y": 0.7071067811865476,
|
|
44
|
+
"z": 0,
|
|
45
|
+
"w": -0.7071067811865475
|
|
46
|
+
},
|
|
47
|
+
"scale": {
|
|
48
|
+
"x": 1,
|
|
49
|
+
"y": 1,
|
|
50
|
+
"z": 1
|
|
51
|
+
},
|
|
52
|
+
"parent": 0
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"name": "asset-packs::Script",
|
|
59
|
+
"data": {
|
|
60
|
+
"0": {
|
|
61
|
+
"json": {
|
|
62
|
+
"value": [
|
|
63
|
+
{
|
|
64
|
+
"path": "{assetPath}/scripts/seat.ts",
|
|
65
|
+
"priority": 0,
|
|
66
|
+
"layout": "{\"params\":{},\"actions\":[]}"
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "fe41823d-a0ef-4152-bf66-b9f44cdcffa2",
|
|
3
|
+
"name": "Simple Chair",
|
|
4
|
+
"description": "A simple lightweight chair. Players can sit on it.",
|
|
5
|
+
"category": "custom",
|
|
6
|
+
"tags": [
|
|
7
|
+
"seat",
|
|
8
|
+
"furniture",
|
|
9
|
+
"sit"
|
|
10
|
+
],
|
|
11
|
+
"version": "1.1.0",
|
|
12
|
+
"changelog": [
|
|
13
|
+
{
|
|
14
|
+
"version": "1.1.0",
|
|
15
|
+
"notes": "The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see."
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"version": "1.0.1",
|
|
19
|
+
"notes": "Ships the model's texture, which was missing from the folder — without it the model rendered blank white in-world."
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"version": "1.0.0",
|
|
23
|
+
"notes": "Initial release"
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"origin": {
|
|
27
|
+
"source": "builtin"
|
|
28
|
+
},
|
|
29
|
+
"requiredPermissions": [
|
|
30
|
+
"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE",
|
|
31
|
+
"ALLOW_TO_TRIGGER_AVATAR_EMOTE"
|
|
32
|
+
],
|
|
33
|
+
"group": "Seats"
|
|
34
|
+
}
|
|
Binary file
|