a bit of refactoring; importing engine into bun for server

This commit is contained in:
Elizabeth Hunt 2023-07-20 20:47:32 -07:00
parent 0fd9fb0975
commit 72c6c7de12
Signed by: simponic
GPG Key ID: 52B3774857EB24B1
63 changed files with 357 additions and 106 deletions

169
.gitignore vendored Normal file
View File

@ -0,0 +1,169 @@
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
# Logs
logs
_.log
npm-debug.log_
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# Runtime data
pids
_.pid
_.seed
\*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
\*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
\*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
\*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.\*

View File

@ -1,35 +0,0 @@
import { System, SystemNames } from ".";
import { BoundingBox, ComponentNames } from "../components";
import type { Entity } from "../entities";
export class WallBounds extends System {
private screenWidth: number;
constructor(screenWidth: number) {
super(SystemNames.WallBounds);
this.screenWidth = screenWidth;
}
public update(
_dt: number,
entityMap: Map<number, Entity>,
componentEntities: Map<string, Set<number>>
) {
componentEntities.get(ComponentNames.WallBounded)?.forEach((entityId) => {
const entity = entityMap.get(entityId);
if (!entity.hasComponent(ComponentNames.BoundingBox)) {
return;
}
const boundingBox = entity.getComponent<BoundingBox>(
ComponentNames.BoundingBox
);
boundingBox.center.x = Math.min(
this.screenWidth - boundingBox.dimension.width / 2,
Math.max(boundingBox.dimension.width / 2, boundingBox.center.x)
);
});
}
}

View File

@ -7,6 +7,9 @@
"": {
"name": "client",
"version": "0.0.0",
"dependencies": {
"module-alias": "^2.2.3"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^2.0.4",
"@tsconfig/svelte": "^4.0.1",
@ -1585,6 +1588,11 @@
"mkdirp": "bin/cmd.js"
}
},
"node_modules/module-alias": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.3.tgz",
"integrity": "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q=="
},
"node_modules/mri": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",

View File

@ -20,5 +20,8 @@
"tslib": "^2.5.0",
"typescript": "^5.0.2",
"vite": "^4.3.9"
},
"dependencies": {
"module-alias": "^2.2.3"
}
}

View File

@ -1,5 +1,5 @@
import { Floor, Player } from "./entities";
import { Game } from "./Game";
import { Floor, Player } from "@engine/entities";
import { Game } from "@engine/Game";
import {
WallBounds,
FacingDirection,
@ -7,7 +7,7 @@ import {
Physics,
Input,
Collision,
} from "./systems";
} from "@engine/systems";
export class JumpStorm {
private game: Game;

View File

@ -1,10 +1,7 @@
<script lang="ts">
import { onMount } from "svelte";
import { Game } from "../../lib/Game";
import { Render } from "../../lib/systems";
import { Floor } from "../../lib/entities";
import { loadAssets } from "../../lib/config";
import { JumpStorm } from "../../lib/JumpStorm";
import { loadAssets } from "@engine/config";
import { JumpStorm} from "../Jumpstorm";
let canvas: HTMLCanvasElement;
let ctx: CanvasRenderingContext2D;

View File

@ -1,5 +1,5 @@
<script lang="ts">
import { type LeaderBoardEntry } from "../../lib/interfaces";
import { type LeaderBoardEntry } from "@engine/interfaces";
import LeaderBoardCard from "./LeaderBoardCard.svelte";
const MAX_ENTRIES = 8;

View File

@ -1,5 +1,5 @@
<script lang="ts">
import { type LeaderBoardEntry } from "../../lib/interfaces";
import { type LeaderBoardEntry } from "@engine/interfaces";
export let entry: LeaderBoardEntry = {
name: "simponic",

View File

@ -24,5 +24,8 @@
"src/**/*.js",
"src/**/*.svelte"
],
"paths": {
"@engine/*": ["../engine/*"]
},
"references": [{ "path": "./tsconfig.node.json" }]
}

View File

@ -1,7 +1,13 @@
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
import { fileURLToPath, URL } from "node:url";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [svelte()],
})
resolve: {
alias: {
"@engine": fileURLToPath(new URL("../engine", import.meta.url)),
},
},
});

View File

@ -2,18 +2,21 @@ import { Entity } from "./entities";
import { System } from "./systems";
export class Game {
private entities: Map<number, Entity>;
private systems: Map<string, System>;
private systemOrder: string[];
private running: boolean;
private lastTimeStamp: number;
public entities: Map<number, Entity>;
public systems: Map<string, System>;
public componentEntities: Map<string, Set<number>>;
constructor() {
this.running = false;
this.systemOrder = [];
this.systems = new Map();
this.entities = new Map();
this.componentEntities = new Map();
}
public start() {
@ -52,19 +55,22 @@ export class Game {
const dt = timeStamp - this.lastTimeStamp;
this.lastTimeStamp = timeStamp;
const componentEntities = new Map<string, Set<number>>();
this.componentEntities.clear();
this.entities.forEach((entity) =>
entity.getComponents().forEach((component) => {
if (!componentEntities.has(component.name)) {
componentEntities.set(component.name, new Set<number>([entity.id]));
if (!this.componentEntities.has(component.name)) {
this.componentEntities.set(
component.name,
new Set<number>([entity.id])
);
return;
}
componentEntities.get(component.name).add(entity.id);
this.componentEntities.get(component.name).add(entity.id);
})
);
this.systemOrder.forEach((systemName) => {
this.systems.get(systemName).update(dt, this.entities, componentEntities);
this.systems.get(systemName).update(dt, this);
});
};
}

View File

@ -7,13 +7,14 @@ import {
Velocity,
Moment,
} from "../components";
import { Game } from "../Game";
import { PhysicsConstants } from "../config";
import { Entity } from "../entities";
import type { Dimension2D } from "../interfaces";
import { QuadTree } from "../structures";
export class Collision extends System {
private static readonly COLLIDABLE_COMPONENTS = [
private static readonly COLLIDABLE_COMPONENT_NAMES = [
ComponentNames.Collide,
ComponentNames.TopCollidable,
];
@ -33,19 +34,17 @@ export class Collision extends System {
);
}
public update(
dt: number,
entityMap: Map<number, Entity>,
entityComponents: Map<string, Set<number>>
) {
public update(dt: number, game: Game) {
// rebuild the quadtree
this.quadTree.clear();
const entitiesToAddToQuadtree: Entity[] = [];
Collision.COLLIDABLE_COMPONENTS.map((componentName) =>
entityComponents.get(componentName)
Collision.COLLIDABLE_COMPONENT_NAMES.map((componentName) =>
game.componentEntities.get(componentName)
).forEach((entityIds: Set<number>) =>
entityIds.forEach((id) => {
const entity = entityMap.get(id);
const entity = game.entities.get(id);
if (!entity.hasComponent(ComponentNames.BoundingBox)) {
return;
}
@ -65,13 +64,15 @@ export class Collision extends System {
);
});
// find colliding entities and perform collisions
const collidingEntities = this.getCollidingEntities(
entitiesToAddToQuadtree,
entityMap
game.entities
);
collidingEntities.forEach(([entityAId, entityBId]) => {
const [entityA, entityB] = [entityAId, entityBId].map((id) =>
entityMap.get(id)
game.entities.get(id)
);
this.performCollision(entityA, entityB);
});
@ -92,7 +93,7 @@ export class Collision extends System {
entityB.hasComponent(ComponentNames.TopCollidable) &&
entityABoundingBox.center.y <= entityBBoundingBox.center.y &&
velocity &&
velocity.dCartesian.dy >= 0 // don't apply floor logic when coming through the bottom
velocity.dCartesian.dy >= 0 // don't apply "floor" logic when coming through the bottom
) {
if (entityBBoundingBox.rotation != 0) {
throw new Error(
@ -157,11 +158,11 @@ export class Collision extends System {
return collidingEntityIds;
}
// ramblings: https://excalidraw.com/#json=z-xD86Za4a3duZuV2Oky0,KaGe-5iHJu1Si8inEo4GLQ
private getDyToPushOutOfFloor(
entityBoundingBox: BoundingBox,
floorBoundingBox: BoundingBox
): number {
// ramblings: https://excalidraw.com/#json=z-xD86Za4a3duZuV2Oky0,KaGe-5iHJu1Si8inEo4GLQ
const {
rotation,
center: { x, y },

View File

@ -3,6 +3,7 @@ import {
Velocity,
FacingDirection as FacingDirectionComponent,
} from "../components";
import { Game } from "../Game";
import type { Entity } from "../entities";
import { System, SystemNames } from "./";
@ -11,15 +12,11 @@ export class FacingDirection extends System {
super(SystemNames.FacingDirection);
}
public update(
_dt: number,
entityMap: Map<number, Entity>,
componentEntities: Map<string, Set<number>>
) {
componentEntities
public update(_dt: number, game: Game) {
game.componentEntities
.get(ComponentNames.FacingDirection)
?.forEach((entityId) => {
const entity = entityMap.get(entityId);
const entity = game.entities.get(entityId);
if (!entity.hasComponent(ComponentNames.Velocity)) {
return;
}

View File

@ -6,6 +6,7 @@ import {
Velocity,
Mass,
} from "../components";
import { Game } from "../Game";
import { KeyConstants, PhysicsConstants } from "../config";
import type { Entity } from "../entities";
import { Action } from "../interfaces";
@ -34,13 +35,9 @@ export class Input extends System {
return keys.some((key) => this.keys.has(key));
}
public update(
dt: number,
entityMap: Map<number, Entity>,
componentEntities: Map<string, Set<number>>
) {
componentEntities.get(ComponentNames.Control)?.forEach((entityId) => {
const entity = entityMap.get(entityId);
public update(dt: number, game: Game) {
game.componentEntities.get(ComponentNames.Control)?.forEach((entityId) => {
const entity = game.entities.get(entityId);
if (!entity.hasComponent(ComponentNames.Velocity)) {
return;
}
@ -58,8 +55,8 @@ export class Input extends System {
}
});
componentEntities.get(ComponentNames.Jump)?.forEach((entityId) => {
const entity = entityMap.get(entityId);
game.componentEntities.get(ComponentNames.Jump)?.forEach((entityId) => {
const entity = game.entities.get(entityId);
const jump = entity.getComponent<Jump>(ComponentNames.Jump);
const velocity = entity.getComponent<Velocity>(ComponentNames.Velocity);

View File

@ -12,19 +12,16 @@ import {
import { PhysicsConstants } from "../config";
import type { Entity } from "../entities";
import type { Force2D } from "../interfaces";
import { Game } from "../Game";
export class Physics extends System {
constructor() {
super(SystemNames.Physics);
}
public update(
dt: number,
entityMap: Map<number, Entity>,
componentEntities: Map<string, Set<number>>
): void {
componentEntities.get(ComponentNames.Forces)?.forEach((entityId) => {
const entity = entityMap.get(entityId);
public update(dt: number, game: Game): void {
game.componentEntities.get(ComponentNames.Forces)?.forEach((entityId) => {
const entity = game.entities.get(entityId);
const mass = entity.getComponent<Mass>(ComponentNames.Mass).mass;
const forces = entity.getComponent<Forces>(ComponentNames.Forces).forces;
@ -74,8 +71,8 @@ export class Physics extends System {
}
});
componentEntities.get(ComponentNames.Velocity)?.forEach((entityId) => {
const entity = entityMap.get(entityId);
game.componentEntities.get(ComponentNames.Velocity)?.forEach((entityId) => {
const entity = game.entities.get(entityId);
const velocity = entity.getComponent<Velocity>(ComponentNames.Velocity);
const boundingBox = entity.getComponent<BoundingBox>(
ComponentNames.BoundingBox

View File

@ -1,7 +1,9 @@
import { System, SystemNames } from ".";
import { BoundingBox, ComponentNames, Sprite } from "../components";
import type { Entity } from "../entities";
import { Game } from "../Game";
import type { DrawArgs } from "../interfaces";
import { clamp } from "../utils";
export class Render extends System {
private ctx: CanvasRenderingContext2D;
@ -11,15 +13,11 @@ export class Render extends System {
this.ctx = ctx;
}
public update(
dt: number,
entityMap: Map<number, Entity>,
componentEntities: Map<string, Set<number>>
) {
public update(dt: number, game: Game) {
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
componentEntities.get(ComponentNames.Sprite)?.forEach((entityId) => {
const entity = entityMap.get(entityId);
game.componentEntities.get(ComponentNames.Sprite)?.forEach((entityId) => {
const entity = game.entities.get(entityId);
const sprite = entity.getComponent<Sprite>(ComponentNames.Sprite);
sprite.update(dt);
@ -29,6 +27,22 @@ export class Render extends System {
ComponentNames.BoundingBox
);
// don't render if we're outside the screen
if (
clamp(
boundingBox.center.y,
-boundingBox.dimension.height / 2,
this.ctx.canvas.height + boundingBox.dimension.height / 2
) != boundingBox.center.y ||
clamp(
boundingBox.center.x,
-boundingBox.dimension.width / 2,
this.ctx.canvas.width + boundingBox.dimension.width / 2
) != boundingBox.center.x
) {
return;
}
drawArgs = {
center: boundingBox.center,
dimension: boundingBox.dimension,

View File

@ -1,4 +1,5 @@
import { Entity } from "../entities";
import { Game } from "../Game";
export abstract class System {
public readonly name: string;
@ -7,9 +8,5 @@ export abstract class System {
this.name = name;
}
abstract update(
dt: number,
entityMap: Map<number, Entity>,
componentEntities: Map<string, Set<number>>
): void;
abstract update(dt: number, game: Game): void;
}

View File

@ -0,0 +1,36 @@
import { System, SystemNames } from ".";
import { BoundingBox, ComponentNames } from "../components";
import { Game } from "../Game";
import type { Entity } from "../entities";
import { clamp } from "../utils";
export class WallBounds extends System {
private screenWidth: number;
constructor(screenWidth: number) {
super(SystemNames.WallBounds);
this.screenWidth = screenWidth;
}
public update(_dt: number, game: Game) {
game.componentEntities
.get(ComponentNames.WallBounded)
?.forEach((entityId) => {
const entity = game.entities.get(entityId);
if (!entity.hasComponent(ComponentNames.BoundingBox)) {
return;
}
const boundingBox = entity.getComponent<BoundingBox>(
ComponentNames.BoundingBox
);
boundingBox.center.x = clamp(
boundingBox.center.x,
boundingBox.dimension.width / 2,
this.screenWidth - boundingBox.dimension.width / 2
);
});
}
}

2
engine/utils/clamp.ts Normal file
View File

@ -0,0 +1,2 @@
export const clamp = (num: number, min: number, max: number) =>
Math.min(Math.max(num, min), max);

View File

@ -1,3 +1,4 @@
export * from "./rotateVector";
export * from "./normalizeVector";
export * from "./dotProduct";
export * from "./clamp";

15
server/README.md Normal file
View File

@ -0,0 +1,15 @@
# server
To install dependencies:
```bash
bun install
```
To run:
```bash
bun run index.ts
```
This project was created using `bun init` in bun v0.6.14. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.

BIN
server/bun.lockb Executable file

Binary file not shown.

0
server/index.ts Normal file
View File

13
server/package.json Normal file
View File

@ -0,0 +1,13 @@
{
"name": "server",
"module": "src/index.ts",
"type": "module",
"devDependencies": {
"bun-types": "^0.6.14"
},
"peerDependencies": {
"typescript": "^5.0.0"
},
"dependencies": {
}
}

3
server/src/index.ts Normal file
View File

@ -0,0 +1,3 @@
import { Game } from "../../engine/Game";
console.log(Game);

21
server/tsconfig.json Normal file
View File

@ -0,0 +1,21 @@
{
"compilerOptions": {
"lib": ["ESNext"],
"module": "esnext",
"target": "esnext",
"moduleResolution": "bundler",
"moduleDetection": "force",
"allowImportingTsExtensions": true,
"strict": true,
"downlevelIteration": true,
"skipLibCheck": true,
"jsx": "preserve",
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"allowJs": true,
"noEmit": true,
"types": [
"bun-types" // add Bun global
]
}
}