Compare commits

..

No commits in common. "add6db3bb6dcc65fdef32060191317e7f08c0c33" and "b1c03da589436c15f2b2dee3efa4895c4a4d7e1c" have entirely different histories.

21 changed files with 314 additions and 109 deletions

1
.env.example Normal file
View file

@ -0,0 +1 @@
ROBLOXSECURITY="_|WARNING:-DO-NOT-SHARE-THIS.--Sharing-this-will-allow-someone-to-log-in-as-you-and-to-steal-your-ROBUX-and-items.|youReallyThoughtBuddy"

6
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "npm" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"

36
.github/workflows/build.yml vendored Normal file
View file

@ -0,0 +1,36 @@
name: Build
on:
pull_request:
push:
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Set Node.js 16.x
uses: actions/setup-node@v3
with:
node-version: 16.x
- name: Run install
uses: borales/actions-yarn@v4
with:
cmd: install # will run `yarn install` command
- name: Install aftman
uses: ok-nick/setup-aftman@v0.3.0
- name: Build
uses: borales/actions-yarn@v4
with:
cmd: build # will run `yarn build` command
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: goopler
path: build.rbxl

37
.github/workflows/deploy.yml vendored Normal file
View file

@ -0,0 +1,37 @@
name: Deploy
on:
pull_request:
push:
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Set Node.js 16.x
uses: actions/setup-node@v3
with:
node-version: 16.x
- name: Run install
uses: borales/actions-yarn@v4
with:
cmd: install # will run `yarn install` command
- name: Install aftman
uses: ok-nick/setup-aftman@v0.3.0
- name: Build
uses: borales/actions-yarn@v4
with:
cmd: build # will run `yarn build` command
- name: Deploy project
run: mantle deploy -e default
env:
ROBLOSECURITY: ${{ secrets.ROBLOSECURITY }}
MANTLE_AWS_ACCESS_KEY_ID: ${{ secrets.MANTLE_AWS_ACCESS_KEY_ID }}
MANTLE_AWS_SECRET_ACCESS_KEY: ${{ secrets.MANTLE_AWS_SECRET_ACCESS_KEY }}

27
.github/workflows/lint.yml vendored Normal file
View file

@ -0,0 +1,27 @@
name: Lint
on:
pull_request:
push:
jobs:
build:
name: ESLint
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Set Node.js 16.x
uses: actions/setup-node@v3
with:
node-version: 16.x
- name: Run install
uses: borales/actions-yarn@v4
with:
cmd: install # will run `yarn install` command
- name: Lint
uses: borales/actions-yarn@v4
with:
cmd: lint # will run `yarn lint` command

14
.gitignore vendored
View file

@ -1,15 +1,21 @@
# Builds
# builds
/out
build.rbxl
*.tsbuildinfo
# Deps
# deps
/node_modules
/include
# Mac junk
# mac users.. stop making these files or im gonna get mad 😡😡😡
.DS_Store
/__MACOSX
# Logs
# logs
*.log
# my secrets 😍😎
.env
# we use remote stuff now
.mantle-state.yml

View file

@ -5,3 +5,4 @@
[tools]
rojo = "rojo-rbx/rojo@7.3.0"
run-in-roblox = "rojo-rbx/run-in-roblox@0.3.0"
mantle = "blake-mealey/mantle@0.11.9"

29
mantle.yml Normal file
View file

@ -0,0 +1,29 @@
environments:
- label: default
target:
experience:
icon: marketing/gameIcon.jpg
thumbnails:
- marketing/gameThumbnailDefault.jpg
configuration:
genre: fps
playableDevices: [computer]
privateServers:
price: 50
enableStudioAccessToApis: true
avatarType: r6
places:
start:
file: build.rbxl
configuration:
name: Goopler
description: |-
Read my github repo reidlabwastaken/goopler if u can hahaha
maxPlayerCount: 100
state:
remote:
region: us-west-2
bucket: goopler-mantle-states
key: goopler

BIN
marketing/gameIcon.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View file

@ -20,7 +20,8 @@
"watch": "rbxtsc -w --rojo default.project.json --verbose",
"lint": "eslint src tests --max-warnings 0",
"serve": "rojo serve default.project.json",
"test": "yarn run build && run-in-roblox --place build.rbxl --script out/tests/runners/run.server.lua"
"test": "yarn run build && run-in-roblox --place build.rbxl --script out/tests/runners/run.server.lua",
"deploy": "yarn run build && mantle deploy -e default"
},
"devDependencies": {
"@rbxts/compiler-types": "^2.1.1-types.0",
@ -33,7 +34,6 @@
},
"dependencies": {
"@rbxts/character-promise": "^1.0.2",
"@rbxts/log": "^0.6.3",
"@rbxts/matter": "^0.6.2-ts.6",
"@rbxts/plasma": "^0.4.1-ts.1",
"@rbxts/rewire": "^0.3.0",

View file

@ -7,25 +7,32 @@ An in-dev game that I plan to make a shooter game out of.
— reidlab
# Setup
When you first setup this repository, you probably want to add your `.ROBLOSECURITY` cookie into the `.env` file for automatic deployment with [Mantle](https://mantledeploy.vercel.app/). You also should change the ID of the game.
# Hacks
* I get a strange error about private identifiers in [`./src/ReplicatedStorage/ecs/state.ts`](./src/ReplicatedStorage/ecs/state.ts)
* I decided to omit the "TS" folder from [`./default.project.json:40`](./default.project.json) due to the script override not working in Health.server.ts in StarterCharacterScripts.
# Todo
### High priority
* Fix automatic deployment. Its fixed itself, but its broken again. By the time this commit goes through I bet it will work again. Honestly super weird. Just make it consistant
* Add tests
* Add guns. Try it in default roblox-ts and slowly reimplement it into our component system.
* Add guns. Try it in default roblox-ts and slowly reimplement it into our component system
#### Medium priority
* Crouching
* Animations
* Change remote state to a provider where I dont have to worry about money
* Seperate dev&prod environments (maybe later??? the game is very early stage so idk)
* Migrate todo to somewhere else like the issues tab
##### Low priority
* Camera bobble
* Add the bound tags in [`./src/ReplicatedStorage/ecs/boundTags.ts`](./src/ReplicatedStorage/ecs/boundTags.ts)
* Cooler sprinting! (Tween fov and speed)
* Cooler sprinting!! (Tween fov and speed)
* Crouching?
# Fixes
### High Priority
* Currently, when resetting, sometimes your health goes back up. This is due to the reconciliation of health. Simply put, your health is not being set to zero inside of our entity component system, due to us not having the reset event currently like that. See it here: [StarterGui.SetCore](https://create.roblox.com/docs/reference/engine/classes/StarterGui#SetCore) It uses BindableEvents.
* Currently, when resetting, sometimes your health goes back up. This is due to the reconciliation of health. Simply put, your health is not being set to zero inside of our entity component system, due to us not having the reset event currently like that. See it here: [StarterGui.SetCore](https://create.roblox.com/docs/reference/engine/classes/StarterGui#SetCore) It uses BindableEvents and stuff idk
#### Medium priority
##### Low priority

View file

@ -45,9 +45,9 @@ export function start<S extends object>(host: Host, state: S): [World, S] {
})
if (host === Host.All || host === Host.Server) {
const ServerState = state as serverState
const _ServerState = state as serverState
startTags(world, tags, ServerState)
startTags(world, tags)
}
if (host === Host.All || host === Host.Client) {

View file

@ -6,17 +6,27 @@ import { clientState } from "./state"
type ComponentNames = keyof typeof Components
type ComponentConstructors = (typeof Components)[ComponentNames]
const DEBUG_SPAWN = "Spawn %ds%d with %s"
const DEBUG_DESPAWN = "Despawn %ds%d"
const DEBUG_MODIFY = "Modify %ds%d adding %s, removing %s"
let connection: RBXScriptConnection | undefined
/**
* Starts the replication receiver.
*
* @param world - The world to replicate components in
* @param client - The client state for the ECS
* @param ClientState - The client state for the ECS
*/
export function start(world: World, client: clientState): void {
export function start(world: World, ClientState: clientState): void {
if (connection) return
function debugPrint(message: string, args: () => (string | number)[]): void {
if (ClientState.debugEnabled) {
print("ECS Replication>", string.format(message, ...args()))
}
}
const replicationEvent = waitForEvent("EcsReplication")
const serverToClientEntity = new Map<string, AnyEntity>()
@ -28,11 +38,7 @@ export function start(world: World, client: clientState): void {
if (clientId !== undefined && next(componentMap)[0] === undefined) {
world.despawn(clientId)
serverToClientEntity.delete(serverId)
client.logger.Debug(
"ECS Replication> Despawn {@clientId}s{@serverId}",
clientId,
serverId
)
debugPrint(DEBUG_DESPAWN, () => [clientId, serverId])
continue
}
@ -66,12 +72,7 @@ export function start(world: World, client: clientState): void {
if (clientId === undefined) {
const clientId = world.spawn(...componentsToInsert)
serverToClientEntity.set(serverId, clientId)
client.logger.Debug(
"ECS Replication> Spawn {@clientId}s{@serverId} with {@insertNames}",
clientId,
serverId,
insertNames.join(",")
)
debugPrint(DEBUG_SPAWN, () => [clientId, serverId, insertNames.join(",")])
} else {
if (componentsToInsert.size() > 0) {
world.insert(clientId, ...componentsToInsert)
@ -81,13 +82,12 @@ export function start(world: World, client: clientState): void {
world.remove(clientId, ...componentsToRemove)
}
client.logger.Debug(
"ECS Replication> Modify {@clientId}s{serverId} adding {@insertNames}, removing {@removeNames}",
debugPrint(DEBUG_MODIFY, () => [
clientId,
serverId,
insertNames.size() > 0 ? insertNames.join(", ") : "nothing",
removeNames.size() > 0 ? removeNames.join(", ") : "nothing"
)
])
}
}
}

View file

@ -1,54 +1,23 @@
/* eslint-disable roblox-ts/no-private-identifier */
import { CharacterRigR6 } from "@rbxts/character-promise"
import { Logger } from "@rbxts/log"
import { InputKind } from "ReplicatedStorage/inputKind"
/**
* The client ECS state.
*/
export class clientState {
constructor(
player: Player,
character: CharacterRigR6,
debugEnabled: boolean,
isRunning: boolean,
// lastProcessedCommand: Inputkind,
logger: Logger
) {
this.character = character
this.player = player
this.debugEnabled = debugEnabled
this.isRunning = isRunning
// this.lastProcessedCommand = lastProcessedCommand
this.logger = logger
}
player: Player
character: CharacterRigR6
debugEnabled: boolean
isRunning: boolean
[index: string]: unknown
character?: CharacterRigR6
player?: Player
debugEnabled = false
isRunning = false
lastProcessedCommand?: InputKind
logger: Logger
}
/**
* The server ECS state.
*/
export class serverState {
constructor(
logger: Logger
) {
this.logger = logger
[index: string]: unknown
}
logger: Logger
}
/**
* The shared ECS state.
*/
export type sharedState = serverState & clientState

View file

@ -2,7 +2,6 @@ import { AnyEntity, Component, World } from "@rbxts/matter"
import { CollectionService } from "@rbxts/services"
import { getIdAttribute } from "ReplicatedStorage/idAttribute"
import { Model, Transform } from "./components"
import { serverState } from "./state"
export type ComponentConstructor<T extends object> = () => Component<T>
@ -16,8 +15,7 @@ const connections: RBXScriptConnection[] = []
*/
export function start(
world: World,
bound: ReadonlyMap<string, ComponentConstructor<object>>,
server: serverState
bound: ReadonlyMap<string, ComponentConstructor<object>>
): void {
function spawnBound<T extends object>(
instance: Instance,
@ -28,13 +26,13 @@ export function start(
if (instance.PrimaryPart) {
primaryPart = instance.PrimaryPart
} else {
server.logger.Warn("Attempted to tag a model that has no primary part: {@instance}", instance)
warn("Attempted to tag a model that has no primary part:", instance)
return
}
} else if (instance.IsA("BasePart")) {
primaryPart = instance
} else {
server.logger.Warn("Attempted to tag an instance that is not a Model or BasePart: {@instance}", instance)
warn("Attempted to tag an instance that is not a Model or BasePart:", instance)
return
}

View file

@ -1,7 +1,6 @@
import { useEvent, World } from "@rbxts/matter"
import { Players } from "@rbxts/services"
import * as Components from "ReplicatedStorage/ecs/components"
import { serverState } from "ReplicatedStorage/ecs/state"
import { getEvent } from "ReplicatedStorage/remotes"
type ComponentName = keyof typeof Components
@ -21,7 +20,7 @@ const replicatedComponents: ReadonlySet<ComponentConstructor> = REPLICATED_COMPO
getEvent("EcsReplication")
function replication(world: World, server: serverState): void {
function replication(world: World): void {
const replicationEvent = getEvent("EcsReplication")
let payload: Map<string, Map<ComponentName, { data?: Components.GooplerComponent }>> | undefined
@ -44,7 +43,7 @@ function replication(world: World, server: serverState): void {
}
}
server.logger.Debug("Sending initial payload to {@player}", player)
print("Sending initial payload to", player)
replicationEvent.FireClient(player, payload)
}

View file

@ -1,4 +1,3 @@
import Log, { Logger } from "@rbxts/log"
import { start } from "ReplicatedStorage/ecs"
import { serverState } from "ReplicatedStorage/ecs/state"
import { Host } from "ReplicatedStorage/hosts"
@ -7,14 +6,7 @@ import { getEvent } from "ReplicatedStorage/remotes"
const HOST = Host.Server
const serverLogger = Logger.configure()
.EnrichWithProperty("Roblox-TS Version", _VERSION)
.WriteTo(Log.RobloxOutput())
.Create()
const ServerState = new serverState(
serverLogger
)
const ServerState = new serverState()
// We only do this here at the moment to create a dummy event for replication.
// In the future this will be created by the replication system.

View file

@ -1,5 +1,4 @@
import { CharacterRigR6 } from "@rbxts/character-promise"
import Log, { Logger } from "@rbxts/log"
import { Players } from "@rbxts/services"
import { start } from "ReplicatedStorage/ecs"
import { clientState } from "ReplicatedStorage/ecs/state"
@ -8,19 +7,11 @@ import { setEnvironment } from "ReplicatedStorage/idAttribute"
const HOST = Host.Client
const clientLogger = Logger.configure()
.EnrichWithProperty("Roblox-TS Version", _VERSION)
.WriteTo(Log.RobloxOutput())
.Create()
const ClientState = new clientState()
const ClientState = new clientState(
Players.LocalPlayer,
(Players.LocalPlayer.Character || Players.LocalPlayer.CharacterAdded.Wait()[0]) as CharacterRigR6,
false,
false,
clientLogger
)
const player = Players.LocalPlayer
ClientState.character = (player.Character || player.CharacterAdded.Wait()[0]) as CharacterRigR6
ClientState.player = player
setEnvironment(HOST)
start(HOST, ClientState)

View file

@ -0,0 +1,118 @@
<roblox xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd" version="4">
<Meta name="ExplicitAutoJoints">true</Meta>
<External>null</External>
<External>nil</External>
<Item class="Part" referent="RBXE7866AEC03004D3B83A1415157AEFC3C">
<Properties>
<bool name="Anchored">false</bool>
<BinaryString name="AttributesSerialize"></BinaryString>
<float name="BackParamA">-0.5</float>
<float name="BackParamB">0.5</float>
<token name="BackSurface">0</token>
<token name="BackSurfaceInput">0</token>
<float name="BottomParamA">-0.5</float>
<float name="BottomParamB">0.5</float>
<token name="BottomSurface">0</token>
<token name="BottomSurfaceInput">0</token>
<CoordinateFrame name="CFrame">
<X>19</X>
<Y>2.00000119</Y>
<Z>22</Z>
<R00>1</R00>
<R01>0</R01>
<R02>0</R02>
<R10>0</R10>
<R11>1</R11>
<R12>0</R12>
<R20>0</R20>
<R21>0</R21>
<R22>1</R22>
</CoordinateFrame>
<bool name="CanCollide">true</bool>
<bool name="CanQuery">true</bool>
<bool name="CanTouch">true</bool>
<bool name="CastShadow">true</bool>
<string name="CollisionGroup">Default</string>
<int name="CollisionGroupId">0</int>
<Color3uint8 name="Color3uint8">4288914085</Color3uint8>
<PhysicalProperties name="CustomPhysicalProperties">
<CustomPhysics>false</CustomPhysics>
</PhysicalProperties>
<bool name="EnableFluidForces">false</bool>
<float name="FrontParamA">-0.5</float>
<float name="FrontParamB">0.5</float>
<token name="FrontSurface">0</token>
<token name="FrontSurfaceInput">0</token>
<float name="LeftParamA">-0.5</float>
<float name="LeftParamB">0.5</float>
<token name="LeftSurface">0</token>
<token name="LeftSurfaceInput">0</token>
<bool name="Locked">false</bool>
<bool name="Massless">false</bool>
<token name="Material">256</token>
<string name="MaterialVariantSerialized"></string>
<string name="Name">NotABasePart</string>
<CoordinateFrame name="PivotOffset">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
<R00>1</R00>
<R01>0</R01>
<R02>0</R02>
<R10>0</R10>
<R11>1</R11>
<R12>0</R12>
<R20>0</R20>
<R21>0</R21>
<R22>1</R22>
</CoordinateFrame>
<float name="Reflectance">0</float>
<float name="RightParamA">-0.5</float>
<float name="RightParamB">0.5</float>
<token name="RightSurface">0</token>
<token name="RightSurfaceInput">0</token>
<int name="RootPriority">0</int>
<Vector3 name="RotVelocity">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<int64 name="SourceAssetId">-1</int64>
<BinaryString name="Tags">RXhhbXBsZQ==</BinaryString>
<float name="TopParamA">-0.5</float>
<float name="TopParamB">0.5</float>
<token name="TopSurface">0</token>
<token name="TopSurfaceInput">0</token>
<float name="Transparency">0</float>
<Vector3 name="Velocity">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<token name="formFactorRaw">1</token>
<token name="shape">1</token>
<Vector3 name="size">
<X>4</X>
<Y>4</Y>
<Z>4</Z>
</Vector3>
</Properties>
<Item class="Decal" referent="RBXC28C593C6B7F48B6AF6E20AAFD33B531">
<Properties>
<BinaryString name="AttributesSerialize"></BinaryString>
<Color3 name="Color3">
<R>1</R>
<G>1</G>
<B>1</B>
</Color3>
<token name="Face">5</token>
<string name="Name">Remilia Scarlet deka fumo</string>
<int64 name="SourceAssetId">9961773472</int64>
<BinaryString name="Tags"></BinaryString>
<Content name="Texture"><url>http://www.roblox.com/asset/?id=9961773438</url></Content>
<float name="Transparency">0</float>
<int name="ZIndex">1</int>
</Properties>
</Item>
</Item>
</roblox>

View file

@ -91,23 +91,11 @@
resolved "https://registry.yarnpkg.com/@rbxts/compiler-types/-/compiler-types-2.1.1-types.0.tgz#a1f02b57402dffec474dd6656ec1d8a897b9756b"
integrity sha512-wBRma9MgPbOxvCaQEUvraHLHAmLFGW9R6fT65+MBu3uCYM6vUNWj8l4dHRxgkUK8lnGYdGWxsr/sZFk8sdvwog==
"@rbxts/log@^0.6.3":
version "0.6.3"
resolved "https://registry.yarnpkg.com/@rbxts/log/-/log-0.6.3.tgz#65b51a897a2d646457db95b563ade3313e08d0be"
integrity sha512-YZpDvjL7yif9aYuNAkuM9vnegcwQmOwX0CfvGfWvrzCpmARY4Ey2pTMhoEgxKo36HcdPPi3aLxmcuvn0NHrPPg==
dependencies:
"@rbxts/message-templates" "^0.3.1"
"@rbxts/matter@^0.6.2-ts.6":
version "0.6.4"
resolved "https://registry.yarnpkg.com/@rbxts/matter/-/matter-0.6.4.tgz#49ff6ce56bada1ce7c5e2715a05daaa3fb7615e6"
integrity sha512-84naXqNpUfb5aCEcKf99wdqNnNAuwXh4B73GMQBzrUGiF70m0EWTdmm0qHihdlghGPrCRBSFeYK5esMJvKs/SQ==
"@rbxts/message-templates@^0.3.1":
version "0.3.2"
resolved "https://registry.yarnpkg.com/@rbxts/message-templates/-/message-templates-0.3.2.tgz#85169980cf73f659282aa9846290ceaf06967167"
integrity sha512-79onYskH3pgrBT73Zs+biQ31vAVvupKQaxGNWGjyGsxwNhO2YaN/qkut0bvOshaGa+ZzqAXApRVrN8ifIMPiMQ==
"@rbxts/plasma@^0.4.1-ts.1":
version "0.4.1-ts.1"
resolved "https://registry.yarnpkg.com/@rbxts/plasma/-/plasma-0.4.1-ts.1.tgz#3d8db367c3220e6b6953cdddbf8af9f087165392"