Implement manager concept

This commit is contained in:
Aelita4 2024-09-21 20:59:04 +02:00
parent 56eabc303e
commit cc5d482a0f
Signed by: Aelita4
GPG Key ID: E44490C2025906C1
57 changed files with 1051 additions and 620 deletions

View File

@ -3,6 +3,7 @@ import { getHighestWeightedLanguage, getLocales, getName } from '../lib/utils/la
interface Props {
id: string;
level: string;
name: string;
description: string;
image: string;
@ -17,7 +18,7 @@ const lang = await getLocales(await getHighestWeightedLanguage(Astro.request.hea
<div class="item-card" data-id={Astro.props.id}>
<img class="item-card-image" src={Astro.props.image} />
<div class="item-card-main-field">
<div class="item-card-name">{Astro.props.name}</div>
<div class="item-card-name">{Astro.props.name} | {Astro.props.level}</div>
<div class="item-card-description">{Astro.props.description}</div>
<a id={`button_${Astro.props.id}`} href="#" class="item-card-build">{getName(lang, Astro.props.button_type, Astro.props.button_name)}</a>
<div class="item-card-info-button">i</div>

View File

@ -1,20 +1,26 @@
---
import { ObjectId } from 'mongodb';
import { calculateCurrentAvailableResources } from '../lib/utils/resourceManager';
import { getHighestWeightedLanguage, getLocales, getName } from '../lib/utils/langDriver';
import { getAllResources } from '../lib/db/resources';
import locationManager from '../lib/classes/managers/LocationManager';
import { Resource } from '../lib/classes/managers/ResourceManager';
const resourceTypes = await getAllResources();
const lang = await getLocales(await getHighestWeightedLanguage(Astro.request.headers.get('accept-language')));
const resources = await calculateCurrentAvailableResources(new ObjectId(Astro.cookies.get('planetid')?.value ?? ''));
const planetId = new ObjectId(Astro.cookies.get('planetid')?.value ?? '');
const resourceArray = [];
for(const key in resources) {
resourceArray.push(resources[key as never]);
const resources = locationManager.getPlanet(planetId)?.resources;
if(!resources) return;
resources.calculateCurrentAvailableResources();
const resourceArray: Resource[] = [];
for(const key of resources.resources) {
resourceArray.push(key);
}
---
<div id="resourcebar">
<div class="resourcebar-item-identifier">
@ -23,16 +29,16 @@ for(const key in resources) {
<div id="resourcebar-elements" class="resourcebar-elements">
{resourceArray.map(res =>
<div class="resourcebar-item"
data-res-type={resourceTypes.find(x => x.id === res.name)?.type ?? "solid"}
data-res-type={resourceTypes.find(x => x.id === res.id)?.type ?? "solid"}
data-res-amount={res.amount}
data-res-mining-rate={res.perHourMiningRate}
style={(resourceTypes.find(x => x.id === res.name)?.type ?? "solid") === "solid" ? "" : "display: none;"}
style={(resourceTypes.find(x => x.id === res.id)?.type ?? "solid") === "solid" ? "" : "display: none;"}
>
<div class="resourcebar-item-icon">
<img src={resourceTypes.find(x => x.id === res.name)?.icon ?? "#"} alt={res.name} />
<img src={resourceTypes.find(x => x.id === res.id)?.icon ?? "#"} alt={res.id} />
</div>
<div class="resourcebar-item-text-wrapper" data-resname={res.name}>
<div class="resourcebar-item-text">{getName(lang, 'resources', res.name)}</div>
<div class="resourcebar-item-text-wrapper" data-resname={res.id}>
<div class="resourcebar-item-text">{getName(lang, 'resources', res.id)}</div>
<div class="resourcebar-item-amount">[fetching]</div>
</div>
<div class="resourcebar-item-tooltip">

View File

@ -0,0 +1,70 @@
import DBBuilding from "../../types/db/DBBuilding";
import BuildingManager from "./managers/BuildingManager";
export default class Building {
manager: BuildingManager
data: DBBuilding;
level: number;
constructor(manager: BuildingManager, data: DBBuilding, level: number) {
this.manager = manager;
this.data = data;
this.level = level;
}
async checkRequiredResources(level: number): Promise<boolean> {
const resources = await this.manager.planet.resources.calculateCurrentAvailableResources();
const requirements = this.data.requirements.resources;
let canBuild = true;
for(let res of requirements) {
const resource = resources.find(r => r.id === res.id);
if(!resource) return false;
const required = Math.pow(this.data.multiplier, level) * res.amount;
if(resource.amount < required) {
canBuild = false;
break;
}
}
return canBuild;
}
async checkRequirements(): Promise<{ canBuild: boolean, error: string }> {
const playerBuildings = this.manager.buildings;
let playerBuildingsCanBuild = { canBuild: true, missing: "" };
this.data.requirements.buildings.forEach((buildingReq: any) => {
if(playerBuildings.filter((building) => building.data.id === buildingReq.id)[0]?.level ?? 0 < buildingReq.level) {
playerBuildingsCanBuild = { canBuild: false, missing: `${buildingReq.id} level ${buildingReq.level} required, found ${playerBuildings.filter((building) => building.data.id === buildingReq.id)[0]?.level ?? 0}` };
return;
}
});
if(!playerBuildingsCanBuild.canBuild) return {
canBuild: false,
error: playerBuildingsCanBuild.missing
}
// research
const playerResearch = this.manager.planet.manager.owner.research;
let playerResearchCanBuild = { canBuild: true, missing: "" };
for(const researchReq of this.data.requirements.research) {
if(playerResearch.research.find((research) => research.id === researchReq.id)?.level ?? 0 < researchReq.level) {
playerResearchCanBuild = { canBuild: false, missing: `${researchReq.id} level ${researchReq.level} required, found ${playerResearch.research.find((research) => research.id === researchReq.id)?.level ?? 0}` };
}
};
if(!playerResearchCanBuild.canBuild) return {
canBuild: false,
error: playerResearchCanBuild.missing
}
return {
canBuild: true,
error: ""
}
}
}

32
src/lib/classes/User.ts Normal file
View File

@ -0,0 +1,32 @@
import { ObjectId } from "mongodb";
import ResearchManager from "./managers/ResearchManager";
import { Planet } from "./managers/PlanetManager";
export default class User {
id: ObjectId;
username: string;
email: string;
createdAt: Date;
updatedAt: Date;
lastLogin: Date;
research: ResearchManager;
mainPlanet!: Planet;
constructor(id: ObjectId, username: string, email: string, createdAt: Date, updatedAt: Date, lastLogin: Date) {
this.id = id;
this.username = username;
this.email = email;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
this.lastLogin = lastLogin;
this.research = new ResearchManager(this);
}
async init() {
await this.research.init();
}
addMainPlanet(planet: Planet) {
this.mainPlanet = planet;
}
}

View File

@ -0,0 +1,48 @@
import { getAllBuildings } from '../../db/buildings';
import DBBuilding from '../../../types/db/DBBuilding';
import { Planet } from './PlanetManager';
import { updatePlanetBuildings } from '../../db/planets';
import Building from '../Building';
export default class BuildingManager {
buildings: Array<Building> = [];
buildingsDB: Array<DBBuilding> = []
planet: Planet;
constructor(planet: Planet) {
this.planet = planet;
}
async init(buildingData: { id: string, level: number }[]) {
this.buildingsDB = await getAllBuildings();
buildingData.forEach(building => {
const buildingToFind = this.buildingsDB.find(b => b.id === building.id);
if(buildingToFind) this.buildings.push(new Building(
this,
buildingToFind,
building.level
))
})
return this;
}
getBuildingById(id: string) {
return this.buildings.find(building => building.data.id === id);
}
addBuilding(building: Building) {
const findBuilding = this.buildings.find(b => b.data.id === building.data.id);
if(!findBuilding) this.buildings.push(building);
else findBuilding.level++;
}
removeBuilding(id: string) {
this.buildings = this.buildings.filter(building => building.data.id !== id);
}
async sync() {
await updatePlanetBuildings(this.planet._id, this.buildings.map(building => { return { id: building.data.id, level: building.level } }));
}
}

View File

@ -0,0 +1,153 @@
import { ObjectId } from "mongodb";
import PlanetManager, { Planet } from "./PlanetManager";
import { getAllGalaxies } from "../../db/galaxies";
import { getSectorById } from "../../db/sectors";
import { getSystemById } from "../../db/systems";
import { getAllUsers } from "../../db/users";
import User from "../User";
export type Galaxy = {
_id: ObjectId,
name: string,
sectors: Array<Sector>
}
export type Sector = {
_id: ObjectId,
galaxy: Galaxy,
name: string,
expedition: null,
systems: Array<System>
}
export type System = {
_id: ObjectId,
sector: Sector,
name: string,
ownedBy: User,
planets: PlanetManager
}
class LocationManager {
private static instance: LocationManager;
private constructor() {}
public static getInstance(): LocationManager {
if (!LocationManager.instance) {
LocationManager.instance = new LocationManager();
}
return LocationManager.instance;
}
galaxies: Galaxy[] = [];
users: User[] = [];
async init() {
const users = await getAllUsers();
users.forEach(async user => {
this.users.push(new User(user._id, user.username, user.email, user.createdAt, user.updatedAt, user.lastLogin));
});
const galaxies = await getAllGalaxies();
for(const galaxy of galaxies) {
const galaxyObject: Galaxy = {
_id: galaxy._id,
name: galaxy.name,
sectors: []
};
for(const sectorId of galaxy.sectors) {
const sectorData = await getSectorById(sectorId);
const sectorObject: Sector = {
_id: sectorData._id,
galaxy: galaxyObject,
name: sectorData.name,
expedition: null,
systems: []
};
for(const systemId of sectorData.systems) {
const systemData = await getSystemById(systemId);
const user = this.users.find(user => user.id.equals(systemData.ownedBy));
if(!user) throw new Error("User not found");
const systemObject: System = {
_id: systemData._id,
sector: sectorObject,
name: systemData.name,
ownedBy: user,
planets: new PlanetManager(user)
};
await systemObject.planets.fillData(systemData.planets);
sectorObject.systems.push(systemObject);
};
galaxyObject.sectors.push(sectorObject);
};
this.galaxies.push(galaxyObject);
};
for(const user of this.users) {
await user.init();
const userDB = users.find(u => u._id.equals(user.id));
const mainPlanet = this.getPlanet(userDB?.mainPlanet as ObjectId);
if(!mainPlanet) throw new Error("Main planet not found");
user.mainPlanet = mainPlanet;
};
return this;
}
getGalaxy(_id: ObjectId) {
return this.galaxies.find(galaxy => galaxy._id.equals(_id));
}
getSector(_id: ObjectId) {
let foundSector: Sector | undefined;
this.galaxies.find(galaxy => galaxy.sectors.find(sector => {
if(sector._id.equals(_id)) foundSector = sector;
}));
return foundSector;
}
getSystem(_id: ObjectId) {
let foundSystem: System | undefined;
this.galaxies.find(galaxy => galaxy.sectors.find(sector => sector.systems.find(system => {
if(system._id.equals(_id)) foundSystem = system
})));
return foundSystem
}
getPlanet(_id: ObjectId) {
let foundPlanet: Planet | undefined;
for(const galaxy of this.galaxies) {
for(const sector of galaxy.sectors) {
for(const system of sector.systems) {
foundPlanet = system.planets.getPlanetById(_id);
if(foundPlanet) break;
}
if(foundPlanet) break;
}
if(foundPlanet) break;
}
return foundPlanet;
}
getUser(_id: ObjectId) {
return this.users.find(user => user.id.equals(_id));
}
}
const locationManager = LocationManager.getInstance();
locationManager.init();
export default locationManager;

View File

@ -0,0 +1,51 @@
import { ObjectId } from "mongodb";
import BuildingManager from "./BuildingManager";
import { getPlanetById } from "../../db/planets";
import ResourceManager from "./ResourceManager";
import User from "../User";
export type Planet = {
_id: ObjectId;
manager: PlanetManager;
name: string;
fields: number;
resources: ResourceManager;
buildings: BuildingManager;
ships: Array<any>;
}
export default class PlanetManager {
planets: Array<Planet> = [];
owner: User;
constructor(user: User) {
this.owner = user;
}
async fillData(planets: ObjectId[]) {
await Promise.all(planets.map(async planetId => {
const planet = await getPlanetById(planetId);
const planetObject: Planet = {
_id: planet._id,
manager: this,
name: planet.name,
fields: planet.fields,
//@ts-ignore
resources: null,
//@ts-ignore
buildings: null,
ships: planet.ships
}
planetObject.resources = await new ResourceManager(planetObject).init(planet.resources);
planetObject.buildings = await new BuildingManager(planetObject).init(planet.buildings);
this.planets.push(planetObject);
}));
}
getPlanetById(id: ObjectId) {
return this.planets.find(planet => planet._id.equals(id));
}
}

View File

@ -0,0 +1,50 @@
import DBResearch from "../../../types/db/DBResearch";
import { getAllResearch } from "../../db/research";
import { getUserResearch, updateUserResearch } from "../../db/users";
import User from "../User";
export type Research = {
id: string,
level: number,
data: DBResearch
}
export default class ResearchManager {
research: Array<Research> = [];
researchDB: Array<DBResearch> = [];
owner: User;
constructor(user: User) {
this.owner = user;
}
async init() {
this.researchDB = await getAllResearch();
const research = await getUserResearch(this.owner);
this.research = research.map(r => {
const researchData = this.researchDB.find(research => research.id === r.id);
if(!researchData) throw new Error(`Research ${r.id} not found`);
return {
id: r.id,
level: r.level,
data: researchData
}
});
return this;
}
getResearchById(id: string) {
return this.research.find(research => research.data.id === id);
}
addResearch(research: Research) {
const findResearch = this.research.find(r => r.data.id === research.data.id);
if(!findResearch) this.research.push(research);
else findResearch.level++;
}
async sync() {
await updateUserResearch(this.owner, this.research.map(r => { return { id: r.data.id, level: r.level } }));
}
}

View File

@ -0,0 +1,152 @@
import DBResource from "../../../types/db/DBResource";
import { updatePlanetResources } from "../../db/planets";
import { getAllResources } from "../../db/resources";
import { Planet } from "./PlanetManager";
export type Resource = {
id: string,
amount: number,
lastUpdated: Date,
perHourMiningRate: number,
data: DBResource
}
export default class ResourceManager {
resources: Array<Resource> = [];
planet: Planet;
constructor(planet: Planet) {
this.planet = planet;
}
async init(resourceData: { id: string, amount: number, lastUpdated: Date, perHourMiningRate: number }[]) {
const resources = await getAllResources();
if(resourceData.length === 0) {
resourceData = [
{
id: "coal",
amount: 11,
lastUpdated: new Date(),
perHourMiningRate: 11
},
{
id: "iron",
amount: 22,
lastUpdated: new Date(),
perHourMiningRate: 22
},
{
id: "gold",
amount: 33,
lastUpdated: new Date(),
perHourMiningRate: 33
},
{
id: "water",
amount: 44,
lastUpdated: new Date(),
perHourMiningRate: 44
},
{
id: "sulfuric-acid",
amount: 55,
lastUpdated: new Date(),
perHourMiningRate: 55
},
{
id: "liquid-nitrogen",
amount: 66,
lastUpdated: new Date(),
perHourMiningRate: 66
},
{
id: "hydrogen",
amount: 77,
lastUpdated: new Date(),
perHourMiningRate: 77
},
{
id: "oxygen",
amount: 88,
lastUpdated: new Date(),
perHourMiningRate: 88
},
{
id: "helium-3",
amount: 99,
lastUpdated: new Date(),
perHourMiningRate: 99
}
];
await updatePlanetResources(this.planet._id, this.resources);
}
resourceData.forEach(resource => {
const resFromDB = resources.find(res => res.id === resource.id);
if(resFromDB) this.resources.push({
id: resource.id,
amount: resource.amount,
lastUpdated: resource.lastUpdated,
perHourMiningRate: resource.perHourMiningRate,
data: resFromDB
})
});
return this;
}
getResourceById(resId: string) {
return this.resources.find(res => res.id === resId);
}
async calculateCurrentAvailableResources() {
this.resources.forEach(res => {
const timeDiff = Math.abs((new Date()).getTime() - res.lastUpdated.getTime());
const hours = timeDiff / (1000 * 60 * 60);
const amountToAdd = hours * res.perHourMiningRate;
res.amount += amountToAdd;
res.lastUpdated = new Date();
});
await updatePlanetResources(this.planet._id, this.resources);
return this.resources;
}
async getDifference(resources: { id: string, amount: number }[]): Promise<Array<Resource>> {
const currentResources = await this.calculateCurrentAvailableResources();
const difference: Resource[] = [];
currentResources.forEach(res => {
const currentRes = resources.find(r => r.id === res.id);
if(currentRes) difference.push({
id: res.id,
amount: res.amount - currentRes.amount,
lastUpdated: res.lastUpdated,
perHourMiningRate: res.perHourMiningRate,
data: res.data
});
else difference.push(res);
});
return difference;
}
update(resources: Resource[]) {
this.resources = resources;
}
async sync() {
await updatePlanetResources(this.planet._id, this.resources.map(res => {
return {
id: res.id,
amount: res.amount,
lastUpdated: res.lastUpdated,
perHourMiningRate: res.perHourMiningRate
}
}));
}
}

View File

@ -1,7 +1,9 @@
import { AccessTokens } from './mongodb';
import type AccessToken from '../../types/AccessToken';
import { createHash } from 'crypto';
import { ObjectId } from 'mongodb';
import DBAccessToken from '../../types/db/DBAccessToken';
import AccessToken from '../../types/AccessToken';
import locationManager from '../classes/managers/LocationManager';
export const createAccessToken = async (accessToken: AccessToken) => {
const newAccessToken = await (await AccessTokens()).insertOne(accessToken);
@ -23,7 +25,7 @@ export const getAccessToken = async (accessToken: string) => {
{ createdAt },
{ user },
{ entropy }
] }) as Promise<AccessToken | null>;
] }) as Promise<DBAccessToken | null>;
}
export const getAllAccessTokens = async () => {
@ -37,7 +39,24 @@ export const getAllAccessTokens = async () => {
}
const accessTokens = await AccessTokens();
const arrayOfTokens = await accessTokens.find({}).toArray() as AccessToken[];
let arr = [master].concat(arrayOfTokens);
const arrayOfTokens = await accessTokens.find({}).toArray() as DBAccessToken[];
const arr = [master];
for(const token of arrayOfTokens) {
const user = locationManager.getUser(token.user) ?? null;
const tokenObject: AccessToken = {
type: token.type,
user,
entropy: token.entropy,
createdAt: token.createdAt,
expiresAt: token.expiresAt,
createdFrom: token.createdFrom
}
arr.push(tokenObject);
}
return arr;
}

View File

@ -1,4 +1,4 @@
import DBBuilding from '../../types/DBBuilding';
import DBBuilding from '../../types/db/DBBuilding';
import { Buildings } from '../db/mongodb';
export const getAllBuildings = async () => {

13
src/lib/db/galaxies.ts Normal file
View File

@ -0,0 +1,13 @@
import { ObjectId } from "mongodb";
import { Galaxies } from "./mongodb"
import DBGalaxy from "../../types/db/DBGalaxy";
export const getAllGalaxies = async () => {
return await (await Galaxies()).find({}).toArray() as DBGalaxy[];
}
export const getGalaxyById = async (id: ObjectId) => {
return await (await Galaxies()).findOne({
_id: id
}) as DBGalaxy;
}

View File

@ -8,7 +8,6 @@ const mongo = new MongoClient(uri, options);
export const connect = async () => {
await mongo.connect();
// return mongo.db(dbName);
}
export const disconnect = async () => {
@ -30,6 +29,21 @@ export const AccessTokens = async () => {
return db.collection('accessTokens');
}
export const Galaxies = async () => {
const db = await getDB();
return db.collection('galaxies');
}
export const Sectors = async () => {
const db = await getDB();
return db.collection('sectors');
}
export const Systems = async () => {
const db = await getDB();
return db.collection('systems');
}
export const Planets = async () => {
const db = await getDB();
return db.collection('planets');

View File

@ -1,74 +1,29 @@
import { Planets } from '../db/mongodb';
import type User from '../../types/User';
import { ObjectId } from 'mongodb';
import type Planet from '../../types/Planet';
import { getUserById } from './users';
import type Building from '../../types/Building';
import type Ship from '../../types/Ship';
import type PlayerResource from '../../types/PlayerResource';
import DBPlanet from '../../types/db/DBPlanet';
export const getAllPlanets = async (options?: { fetchUserPlanets: boolean }) => {
const planets = await Planets();
const fetched = (await planets.find({}).toArray()).map(async planet => {
planet.owner = await getUserById(planet.owner);
return planet;
})
return Promise.all(fetched) as unknown as Array<Planet>;
}
export const createPlanet = async (options: { name: string, owner?: User, ownerId?: ObjectId, fields?: number }) => {
if(options.owner !== undefined && options.ownerId !== undefined) throw new Error("Duplicate identifier");
let user: ObjectId;
if(options.ownerId) user = options.ownerId;
else if(options.owner) user = options.owner._id;
else throw new Error("Unknown error");
if(options.fields === undefined) options.fields = 20;
//@ts-ignore TODO: find another way of handling IDs from types before inserting to database
const planet = {
owner: user,
name: options.name,
fields: options.fields,
resources: new Array<PlayerResource>,
buildings: new Array<Building>,
ships: new Array<Ship>
}
await (await Planets()).insertOne(planet);
export const getAllPlanets = async () => {
return await (await Planets()).find({}).toArray() as DBPlanet[];
}
export const getPlanetById = async (id: ObjectId) => {
const planets = await Planets();
const planet = await planets.findOne({
_id: id
});
if(!planet) return null;
planet.owner = await getUserById(planet.owner);
return planet as Planet;
return await (await Planets()).findOne({ _id: id }) as DBPlanet;
}
export const createOrUpgradeBuilding = async (planetId: ObjectId, building: Building) => {
const planet = await getPlanetById(planetId);
if(!planet) throw new Error("Planet not found");
const buildingIndex = planet.buildings.findIndex(b => b.id === building.id);
if(buildingIndex === -1) {
planet.buildings.push(building);
} else {
planet.buildings[buildingIndex].level++;
}
export const updatePlanetResources = async (planetId: ObjectId, resources: Array<any>) => {
const planets = await Planets();
await planets.updateOne({
_id: planetId
}, {
await planets.updateOne({ _id: planetId }, {
$set: {
buildings: planet.buildings
resources
}
})
});
}
export const updatePlanetBuildings = async (planetId: ObjectId, buildings: Array<{ id: string, level: number }>) => {
const planets = await Planets();
await planets.updateOne({ _id: planetId }, {
$set: {
buildings
}
});
}

View File

@ -1,4 +1,4 @@
import DBResearch from '../../types/DBResearch';
import DBResearch from '../../types/db/DBResearch';
import { Research } from '../db/mongodb';
export const getAllResearch = async () => {

View File

@ -1,4 +1,4 @@
import DBResource from '../../types/DBResource';
import DBResource from '../../types/db/DBResource';
import { Resources } from '../db/mongodb';
export const getAllResources = async () => {

13
src/lib/db/sectors.ts Normal file
View File

@ -0,0 +1,13 @@
import { ObjectId } from "mongodb";
import { Sectors } from "./mongodb";
import DBSector from "../../types/db/DBSector";
export const getAllSectors = async () => {
return await (await Sectors()).find({}).toArray() as DBSector[];
}
export const getSectorById = async (id: ObjectId) => {
return await (await Sectors()).findOne({
_id: id
}) as DBSector;
}

View File

@ -1,4 +1,4 @@
import DBShip from '../../types/DBShip';
import DBShip from '../../types/db/DBShip';
import { Ships } from '../db/mongodb';
export const getAllShips = async () => {

13
src/lib/db/systems.ts Normal file
View File

@ -0,0 +1,13 @@
import { ObjectId } from "mongodb";
import { Systems } from "./mongodb";
import DBSystem from "../../types/db/DBSystem";
export const getAllSystems = async () => {
return await (await Systems()).find({}).toArray() as DBSystem[];
}
export const getSystemById = async (id: ObjectId) => {
return await (await Systems()).findOne({
_id: id
}) as DBSystem;
}

View File

@ -1,33 +1,30 @@
import { Planets, Users } from '../db/mongodb';
import type User from '../../types/User';
import type AccessToken from '../../types/AccessToken';
import { Users } from '../db/mongodb';
import { ObjectId } from 'mongodb';
import { hash } from 'argon2'
import { createInitialResources } from '../utils/resourceManager';
import type Research from '../../types/Research';
import DBUser from '../../types/db/DBUser';
import User from '../classes/User';
import AccessToken from '../../types/AccessToken';
export const getAllUsers = async () => {
const users = await Users();
return users.find({}).toArray() as Promise<User[]>;
return users.find({}).toArray() as Promise<DBUser[]>;
}
export const createUser = async (username: string, email: string, password: string) => {
const user: User = {
const user = {
username,
email,
password: await hash(password),
lastLogin: new Date(),
createdAt: new Date(),
updatedAt: new Date(),
//@ts-ignore
resources: {}
research: [],
}
await (await Users()).insertOne(user);
const newUser = await getUserByNickOrEmail(username);
if(!newUser) return user;
createInitialResources(newUser._id);
return newUser;
}
@ -40,7 +37,7 @@ export const getUserById = async (id: ObjectId) => {
const users = await Users();
return users.findOne({
_id: id
}) as Promise<User | null>;
}) as Promise<DBUser | null>;
}
export const getUserByNickOrEmail = async (searchString: string) => {
@ -50,14 +47,18 @@ export const getUserByNickOrEmail = async (searchString: string) => {
{ username: searchString },
{ email: searchString }
]
}) as Promise<User | null>;
}) as Promise<DBUser | null>;
}
export const getUserByAccessToken = async(accessToken: string | AccessToken): Promise<User | null> => {
export const getUserByAccessToken = async(accessToken: string | AccessToken): Promise<DBUser | null> => {
if(typeof accessToken === "string") {
const userId = new ObjectId(Buffer.from(accessToken.split(".")[2], 'base64url').toString());
return getUserById(userId);
} else return getUserById(accessToken.user as ObjectId)
} else {
const user = accessToken.user;
if(!user) return null;
return getUserById(user?.id);
}
}
export const updateLastLogin = async (user: User) => {
@ -65,54 +66,14 @@ export const updateLastLogin = async (user: User) => {
return users.updateOne({ username: user.username }, { $set: { lastLogin: new Date() } });
}
export const getUserResearch = async (user: User): Promise<Array<Research>> => {
const research: Array<Research> = [];
if (user?.research !== undefined) {
user?.research.forEach((res: Research) => {
research.push(res);
});
}
return research;
export const getUserResearch = async (user: User): Promise<Array<{ id: string, level: number }>> => {
const users = await Users();
const rawUser = await users.findOne({ username: user.username });
if (!rawUser) return [];
return rawUser.research;
}
export const createOrUpgradeResearch = async (userId: ObjectId, research: Research) => {
export const updateUserResearch = async (user: User, research: Array<{ id: string, level: number }>) => {
const users = await Users();
const result = await users.updateOne(
{ _id: userId, "research.id": research.id },
{ $set: { "research.$.level": research.level } }
);
if (result.modifiedCount === 0) {
await users.updateOne({ _id: userId },
{ $push: { research } }
);
}
}
export const getUserWithPlanets = async (id: ObjectId): Promise<User | null> => {
const users = await Users();
const planets = await Planets();
const rawUser = await users.findOne({
_id: id
})
if (!rawUser) return null;
const userPlanets = (await planets.find({
owner: id
}).toArray()).map(planet => {
planet.owner = rawUser;
return planet;
});
rawUser.planets = {
partial: false,
data: userPlanets
}
return rawUser as User;
users.updateOne({ username: user.username }, { $set: { research } });
}

View File

@ -1,23 +0,0 @@
import type Resource from "../../types/Resource";
import type Resources from "../../types/Resources";
export default function calculateAvailableResources(userResources: Array<Resource>, buildingCost: Array<Resource>) {
let canBuild = true;
const resources = {} as Array<Resource>;
userResources.forEach((userResource) => {
const resource = buildingCost.find((buildingResource) => buildingResource.name === userResource.name);
if (resource) {
resources.push({
name: userResource.name,
amount: userResource.amount - resource.amount
});
if (userResource.amount < resource.amount) canBuild = false;
}
});
return {
canBuild,
resources
};
}

View File

@ -1,100 +0,0 @@
import { ObjectId } from "mongodb"
import { Planets } from "../db/mongodb";
import type PlayerResource from "../../types/PlayerResource";
import { getPlanetById } from "../db/planets";
export const createInitialResources = async (planetId: ObjectId) => {
const resources: Array<PlayerResource> = [
{
name: "coal",
amount: 11,
lastUpdated: new Date(),
perHourMiningRate: 11
},
{
name: "iron",
amount: 22,
lastUpdated: new Date(),
perHourMiningRate: 22
},
{
name: "gold",
amount: 33,
lastUpdated: new Date(),
perHourMiningRate: 33
},
{
name: "water",
amount: 44,
lastUpdated: new Date(),
perHourMiningRate: 44
},
{
name: "sulfuricAcid",
amount: 55,
lastUpdated: new Date(),
perHourMiningRate: 55
},
{
name: "liquidNitrogen",
amount: 66,
lastUpdated: new Date(),
perHourMiningRate: 66
},
{
name: "hydrogen",
amount: 77,
lastUpdated: new Date(),
perHourMiningRate: 77
},
{
name: "oxygen",
amount: 88,
lastUpdated: new Date(),
perHourMiningRate: 88
},
{
name: "helium3",
amount: 99,
lastUpdated: new Date(),
perHourMiningRate: 99
}
];
updatePlanetResources(planetId, resources);
}
export const getResourcesFromPlanet = async (planetId: ObjectId): Promise<Array<PlayerResource> | null> => {
const planet = await getPlanetById(planetId);
if(!planet) return null;
return planet.resources;
}
export const updatePlanetResources = async (planetId: ObjectId, resources: Array<PlayerResource>) => {
const planets = await Planets();
await planets.updateOne({ _id: planetId }, {
$set: {
resources
}
});
}
export const calculateCurrentAvailableResources = async (planetId: ObjectId): Promise<Array<PlayerResource>> => {
const resources = await getResourcesFromPlanet(planetId);
if(resources === null) return [];
resources.forEach(res => {
const timeDiff = Math.abs((new Date()).getTime() - res.lastUpdated.getTime());
const hours = timeDiff / (1000 * 60 * 60);
const amountToAdd = hours * res.perHourMiningRate;
res.amount += amountToAdd;
res.lastUpdated = new Date();
});
updatePlanetResources(planetId, resources);
return resources;
}

View File

@ -1,7 +1,8 @@
import type { ObjectId } from "mongodb";
import type AccessToken from "../../types/AccessToken";
import { ObjectId } from "mongodb";
import AccessToken from "../../types/AccessToken";
import { getAccessToken } from "../db/accessTokens";
import { getUserById } from "../db/users";
import locationManager from "../classes/managers/LocationManager";
export default async function validateAccessToken(request: Request): Promise<Response | AccessToken> {
let accessToken = request.url.split("?")[1]?.split("&").filter((x) => x.split("=")[0] === "token")[0].split("=")[1];
@ -58,5 +59,12 @@ export default async function validateAccessToken(request: Request): Promise<Res
}), { status: 403 }
);
return response;
return {
type: response.type,
user: locationManager.getUser(user._id),
entropy: response.entropy,
createdAt: response.createdAt,
expiresAt: response.expiresAt,
createdFrom: response.createdFrom
} as AccessToken;
}

View File

@ -1,10 +1,10 @@
import { randomBytes, createHash } from "crypto";
import type { APIRoute } from "astro";
import type AccessToken from "../../../types/AccessToken";
import { createAccessToken } from "../../../lib/db/accessTokens";
import { getUserByNickOrEmail } from "../../../lib/db/users";
import config from '../../../../config.json';
import type { ObjectId } from "mongodb";
import AccessToken from "../../../types/AccessToken";
import locationManager from "../../../lib/classes/managers/LocationManager";
export const POST: APIRoute = async({ request }) => {
const data = await request.json().catch(() => {return new Response(
@ -53,16 +53,25 @@ export const POST: APIRoute = async({ request }) => {
const now = new Date();
const timestamp = Buffer.from(String(Date.now())).toString('base64url');
const user = Buffer.from(userFromDb._id?.toString() ?? "").toString('base64url');
const userEncoded = Buffer.from(userFromDb._id?.toString() ?? "").toString('base64url');
const random = randomBytes(16).toString("base64url");
const randomHashed = createHash("sha256").update(random).digest("hex");
const expiresIn = (data.duration ?? 86400) * 1000;
const tokenString = `A.${timestamp}.${user}.${random}`;
const tokenString = `A.${timestamp}.${userEncoded}.${random}`;
const user = locationManager.getUser(userFromDb._id);
if(!user) return new Response(
JSON.stringify({
code: 404,
message: "Not found",
error: `User ${data.username} not found`
}), { status: 404 }
)
const accessToken: AccessToken = {
type: "A",
user: userFromDb._id as ObjectId,
user,
entropy: randomHashed.toString(),
createdAt: now,
expiresAt: new Date(now.getTime() + expiresIn),

View File

@ -1,13 +1,20 @@
import type { APIRoute } from "astro";
import validateAccessToken from "../../../lib/utils/validateAccessToken";
import { getUserByAccessToken } from "../../../lib/db/users";
import type User from "../../../types/User";
export const GET: APIRoute = async({ request }) => {
const response = await validateAccessToken(request);
if(response instanceof Response) return response;
const user = (await getUserByAccessToken(response)) as User;
const user = await getUserByAccessToken(response);
if(!user) return new Response(
JSON.stringify({
code: 404,
message: "Not found",
data: "Access token does not match any user"
}), { status: 404 }
);
return new Response(
JSON.stringify({

View File

@ -1,13 +1,10 @@
import { build, type APIRoute } from "astro";
import { type APIRoute } from "astro";
import validateAccessToken from "../../../lib/utils/validateAccessToken";
import { calculateCurrentAvailableResources, updatePlanetResources } from "../../../lib/utils/resourceManager";
import { getUserByAccessToken, getUserResearch } from "../../../lib/db/users";
import Planet from "../../../types/Planet";
import { createOrUpgradeBuilding, getPlanetById } from "../../../lib/db/planets";
import { getUserByAccessToken } from "../../../lib/db/users";
import { ObjectId } from "mongodb";
import DBResource from "../../../types/PlayerResource";
import { getAllBuildings } from "../../../lib/db/buildings";
import DBBuilding from "../../../types/DBBuilding";
import locationManager from "../../../lib/classes/managers/LocationManager";
import Building from "../../../lib/classes/Building";
import { getAllResources } from "../../../lib/db/resources";
export const POST: APIRoute = async({ request }) => {
const response = await validateAccessToken(request);
@ -36,120 +33,77 @@ export const POST: APIRoute = async({ request }) => {
)
}
const buildingId = body.building;
let buildingData: DBBuilding | null = null;
const userPlanet = locationManager.getPlanet(new ObjectId(body.planet));
const buildings = await getAllBuildings();
buildingData = buildings.find((element) => element.id === buildingId) ?? null;
if(!buildingData) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: "Invalid building id"
}), { status: 400 }
)
}
let userPlanet: Planet | null;
try {
userPlanet = await getPlanetById(new ObjectId(body.planetId));
} catch(e) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: "Invalid planet id"
}), { status: 400 }
)
}
if(!userPlanet) {
return new Response(
JSON.stringify({
code: 404,
message: "Not Found",
error: "Planet not found"
}), { status: 404 }
)
}
// check requirements
// buildings
const playerBuildings = userPlanet.buildings;
buildingData.requirements.buildings.forEach((buildingReq: any) => {
if(playerBuildings.filter((building) => building.id === buildingReq.id)[0].level < buildingReq.level) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: `${buildingReq.id} level ${buildingReq.level} required, found ${playerBuildings.filter((building) => building.id === buildingReq.id)[0].level}`
}), { status: 400 }
)
}
});
// research
const playerResearch = await getUserResearch(user);
buildingData.requirements.research.forEach((researchReq: any) => {
if(playerResearch.filter((research) => research.id === researchReq.id)[0].level < researchReq.level) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: `${researchReq.id} level ${researchReq.level} required, found ${playerResearch.filter((research) => research.id === researchReq.id)[0].level}`
}), { status: 400 }
)
}
});
// resources
const resources = await calculateCurrentAvailableResources(userPlanet._id);
if(!resources) {
return new Response(
JSON.stringify({
code: 500,
message: "Internal Server Error",
error: "Failed to get resources"
}), { status: 500 }
)
}
const playerCurrentResearch = playerResearch.filter((element: any) => element.id === buildingId)[0];
const level = playerCurrentResearch ? playerCurrentResearch.level : 0;
const newResources = structuredClone(resources);
const missingResources: Array<{}> = [];
Object.entries(buildingData.requirements.resources).forEach(([key, value]) => {
const res = resources.filter((element: DBResource) => element.name === value.name)[0];
const cost = playerCurrentResearch ? value.amount * Math.pow((buildingData as {multiplier: number}).multiplier, level) : value.amount;
if(res.amount < cost) {
missingResources.push({
name: key,
required: cost,
available: res.amount
});
return;
}
else newResources.filter((element: DBResource) => element.name === value.name)[0].amount -= cost;
});
if(missingResources.length > 0) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
data: missingResources
error: "Invalid planet ID"
}), { status: 400 }
)
}
await updatePlanetResources(userPlanet._id, newResources);
await createOrUpgradeBuilding(userPlanet._id, { id: buildingId, level: level + 1 });
const buildingId: string = body.building;
const buildingObj = userPlanet.buildings.buildingsDB.find(b => b.id === buildingId);
if(!buildingObj) return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: "Invalid building ID"
}), { status: 400 }
)
const building = new Building(userPlanet.buildings, buildingObj, 1);
const requirements = await building.checkRequirements();
const resources = await building.checkRequiredResources((userPlanet.buildings.getBuildingById(buildingId)?.level ?? 0) + 1);
if(!requirements.canBuild || !resources) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: requirements.error + " | " + resources ? "" : "Not enough resources"
}), { status: 400 }
)
}
const resourcesDiff = await userPlanet.resources.getDifference(building.data.requirements.resources.map(res => {
return {
id: res.id,
amount: Math.pow(building.data.multiplier, (userPlanet.buildings.getBuildingById(buildingId)?.level ?? 0) + 1) * res.amount
}
}));
const resourceDB = await getAllResources();
const resourcesAfter = resourcesDiff.map(res => {
const data = resourceDB.find(r => r.id === res.id);
if(!data) throw new Error("Resource not found");
return {
id: res.id,
amount: res.amount,
lastUpdated: res.lastUpdated,
perHourMiningRate: res.perHourMiningRate,
data
}
});
userPlanet.resources.update(resourcesAfter);
userPlanet.buildings.addBuilding(building);
await userPlanet.buildings.sync();
await userPlanet.resources.sync();
return new Response(
JSON.stringify({
code: 200,
message: "OK"
}), { status: 200 }
)
);
}

View File

@ -1,17 +1,21 @@
import { APIRoute } from "astro";
import { getAllPlanets } from "../../../lib/db/planets";
import Planet from "../../../types/Planet";
import { ObjectId } from "mongodb";
import locationManager from "../../../lib/classes/managers/LocationManager";
export const GET: APIRoute = async ({ request }) => {
const planets = await getAllPlanets();
const response: Array<{planetId: ObjectId, ownerId: ObjectId, name: string}> = [];
planets.forEach((planet: Planet) => {
planets.forEach(planet => {
const playerPlanet = locationManager.getPlanet(planet._id);
if(!playerPlanet) return;
response.push({
planetId: planet._id,
ownerId: planet.owner._id,
ownerId: playerPlanet.manager.owner.id,
name: planet.name,
})
});

View File

@ -1,10 +1,8 @@
import { APIRoute } from "astro";
import { getPlanetById } from "../../../../lib/db/planets";
import { ObjectId } from "mongodb";
import Planet from "../../../../types/Planet";
import { calculateCurrentAvailableResources } from "../../../../lib/utils/resourceManager";
import validateAccessToken from "../../../../lib/utils/validateAccessToken";
import { getUserByAccessToken } from "../../../../lib/db/users";
import locationManager from "../../../../lib/classes/managers/LocationManager";
export const GET: APIRoute = async ({ params, request }) => {
const response = await validateAccessToken(request);
@ -28,36 +26,23 @@ export const GET: APIRoute = async ({ params, request }) => {
}), { status: 400 }
);
let planet: Planet | null;
const planet = locationManager.getPlanet(new ObjectId(planetId));
try {
planet = await getPlanetById(new ObjectId(planetId));
if(!planet) return new Response(
JSON.stringify({
code: 404,
message: "Not Found"
}), { status: 404 }
);
} catch(e) {
return new Response(
JSON.stringify({
code: 500,
message: "Internal Server Error"
}), { status: 500 }
);
}
if(!planet) return new Response(
JSON.stringify({
code: 500,
message: "Internal Server Error"
}), { status: 500 }
);
if(planet.owner._id.toString() !== user._id.toString()) return new Response(
if(!planet.manager.owner.id.equals(user._id)) return new Response(
JSON.stringify({
code: 403,
message: "Forbidden"
}), { status: 403 }
);
await calculateCurrentAvailableResources(planet._id);
//@ts-ignore
delete planet.owner.password;
await locationManager.getPlanet(planet._id)?.resources.calculateCurrentAvailableResources();
return new Response(
JSON.stringify({

View File

@ -1,6 +1,7 @@
import { type APIRoute } from "astro";
import validateAccessToken from "../../../lib/utils/validateAccessToken";
import { getUserByAccessToken, getUserResearch } from "../../../lib/db/users";
import locationManager from "../../../lib/classes/managers/LocationManager";
export const GET: APIRoute = async({ request }) => {
const response = await validateAccessToken(request);
@ -16,11 +17,13 @@ export const GET: APIRoute = async({ request }) => {
)
}
const userResearch = locationManager.getUser(user._id)?.research.research ?? [];
return new Response(
JSON.stringify({
code: 200,
message: "OK",
data: await getUserResearch(user)
data: userResearch
}), { status: 200 }
)
}

View File

@ -1,20 +1,16 @@
import { type APIRoute } from "astro";
import validateAccessToken from "../../../lib/utils/validateAccessToken";
import { createOrUpgradeResearch, getUserByAccessToken, getUserResearch } from "../../../lib/db/users";
import { getPlanetById } from "../../../lib/db/planets";
import Planet from "../../../types/Planet";
import { getUserByAccessToken } from "../../../lib/db/users";
import { ObjectId } from "mongodb";
import { calculateCurrentAvailableResources, getResourcesFromPlanet, updatePlanetResources } from "../../../lib/utils/resourceManager";
import PlayerResource from "../../../types/PlayerResource";
import calculateAvailableResources from "../../../lib/utils/calculateAvailableResources";
import { getAllResearch, getResearchById } from "../../../lib/db/research";
import { getResearchById } from "../../../lib/db/research";
import locationManager from "../../../lib/classes/managers/LocationManager";
export const POST: APIRoute = async({ request }) => {
const response = await validateAccessToken(request);
if(response instanceof Response) return response;
const user = await getUserByAccessToken(response);
if(user === null) {
const userDB = await getUserByAccessToken(response);
if(userDB === null) {
return new Response(
JSON.stringify({
code: 401,
@ -36,8 +32,30 @@ export const POST: APIRoute = async({ request }) => {
)
}
const userPlanet = locationManager.getPlanet(new ObjectId(body.planetId));
if(!userPlanet) {
return new Response(
JSON.stringify({
code: 404,
message: "Not Found",
error: "Planet not found"
}), { status: 404 }
)
}
const user = userPlanet.manager.owner;
if(!user.id.equals(userDB._id)) {
return new Response(
JSON.stringify({
code: 403,
message: "Forbidden",
error: "You don't own this planet"
}), { status: 403 }
)
}
const researchId = body.research;
const researchId = body.research as string;
const research = await getResearchById(researchId);
if(!research) {
@ -49,47 +67,27 @@ export const POST: APIRoute = async({ request }) => {
}), { status: 400 }
)
}
let userPlanet: Planet | null;
try {
userPlanet = await getPlanetById(new ObjectId(body.planetId));
} catch(e) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: "Invalid planet id"
}), { status: 400 }
)
}
if(!userPlanet) {
return new Response(
JSON.stringify({
code: 404,
message: "Not Found",
error: "Planet not found"
}), { status: 404 }
)
}
// check requirements
// buildings
const buildings = userPlanet.buildings;
research.requirements.buildings.forEach((buildingReq) => {
if(buildings.filter((building) => building.id === buildingReq.id)[0]?.level ?? 0 < buildingReq.level) {
for(const buildingReq of research.requirements.buildings) {
if((buildings.buildings.find((building) => building.data.id === buildingReq.id)?.level ?? 0) < buildingReq.level) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: `${buildingReq.id} level ${buildingReq.level} required, found ${buildings.filter((building) => building.id === buildingReq.id)[0]?.level ?? 0}`
error: `${buildingReq.id} level ${buildingReq.level} required, found ${buildings.buildings.filter((building) => building.data.id === buildingReq.id)[0]?.level ?? 0}`
}), { status: 400 }
)
}
});
};
// research
const playerResearch = await getUserResearch(user);
research.requirements.research.forEach((researchReq) => {
if(playerResearch.filter((research) => research.id === researchReq.id)[0].level < researchReq.level) {
const playerResearch = user.research.research;
for(const researchReq of research.requirements.research) {
if((playerResearch.find((research) => research.id === researchReq.id)?.level ?? 0) < researchReq.level) {
return new Response(
JSON.stringify({
code: 400,
@ -98,10 +96,10 @@ export const POST: APIRoute = async({ request }) => {
}), { status: 400 }
)
}
});
};
// resources
const resources = await calculateCurrentAvailableResources(userPlanet._id);
const resources = await userPlanet.resources.calculateCurrentAvailableResources();
if(!resources) {
return new Response(
JSON.stringify({
@ -111,24 +109,25 @@ export const POST: APIRoute = async({ request }) => {
}), { status: 500 }
)
}
const playerCurrentResearch = playerResearch.filter((element: any) => element.id === researchId)[0];
const level = playerCurrentResearch ? playerCurrentResearch.level : 0;
const newResources = structuredClone(resources);
const missingResources: Array<{}> = [];
research.requirements.resources.forEach(resource => {
const res = resources.filter((element: PlayerResource) => element.name === resource.name)[0];
const missingResources: Array<{ id: string, required: number, available: number }> = [];
for(const resource of research.requirements.resources) {
const res = resources.filter((element) => element.id === resource.id)[0];
const cost = playerCurrentResearch ? resource.amount * Math.pow(research.multiplier, level) : resource.amount;
if(res.amount < cost) {
missingResources.push({
name: resource.name,
id: resource.id,
required: cost,
available: res.amount
});
return;
break;
}
else newResources.filter((element: PlayerResource) => element.name === resource.name)[0].amount -= cost;
});
else newResources.filter((element) => element.id === resource.id)[0].amount -= cost;
};
if(missingResources.length > 0) {
return new Response(
@ -139,9 +138,16 @@ export const POST: APIRoute = async({ request }) => {
}), { status: 400 }
)
}
userPlanet.resources.update(newResources);
userPlanet.manager.owner.research.addResearch({
id: researchId,
level: level + 1,
data: research
});
await updatePlanetResources(userPlanet._id, newResources);
await createOrUpgradeResearch(user._id, { id: researchId, level: level + 1 });
await userPlanet.resources.sync();
await userPlanet.manager.owner.research.sync();
return new Response(
JSON.stringify({

View File

@ -6,7 +6,9 @@ import { getUserByAccessToken } from '../../lib/db/users';
import { getHighestWeightedLanguage, getLocales, getName, getObj } from '../../lib/utils/langDriver';
import ResourceBar from '../../components/ResourceBar.astro';
import { getAllBuildings } from '../../lib/db/buildings';
import DBBuilding from '../../types/DBBuilding';
import locationManager from '../../lib/classes/managers/LocationManager';
import { ObjectId } from 'mongodb';
import DBBuilding from '../../types/db/DBBuilding';
const buildingsList = await getAllBuildings();
@ -21,20 +23,31 @@ const locale = await getHighestWeightedLanguage(Astro.request.headers.get('accep
const lang = await getLocales(locale);
const planetId = Astro.cookies.get('planetid')?.value ?? "";
if(planetId === "") return "No planet selected";
const planet = locationManager.getPlanet(new ObjectId(planetId));
if(!planet) return "Planet not found";
const modalSet: { [key: string]: { resources: Array<any>, research: Array<any>, buildings: Array<any>, energy: number } } = {};
for(const building of buildingsList) {
modalSet[building.id] = {
resources: building.requirements.resources,
resources: building.requirements.resources.map(resource => {
return {
id: resource.id,
amount: Math.pow(building.multiplier, (planet.buildings.getBuildingById(building.id)?.level ?? 0) ) * resource.amount
};
}),
research: building.requirements.research,
buildings: building.requirements.buildings,
energy: building.energy
};
}
const buildingsByCategory = buildingsList.reduce((acc: { [key: string]: DBBuilding[] }, building) => {
const buildingsByCategory = buildingsList.reduce((acc: { [key: string]: Array<DBBuilding & { level: number }> }, building) => {
if(!acc[building.category]) acc[building.category] = [];
acc[building.category].push(building);
acc[building.category].push({ ...building, level: planet.buildings.getBuildingById(building.id)?.level ?? 0 });
return acc;
}, {});
---
@ -60,6 +73,7 @@ const buildingsByCategory = buildingsList.reduce((acc: { [key: string]: DBBuildi
{buildings.map(building => (
<BuildingCard
id={building.id}
level={building.level.toString()}
name={getObj(lang, "buildings", building.id).name}
description={getObj(lang, "buildings", building.id).description ?? ""}
image={`/images/buildings/${building.id}.jpeg`}
@ -185,7 +199,7 @@ const buildingsByCategory = buildingsList.reduce((acc: { [key: string]: DBBuildi
color: lime;
}
</style>
<script define:vars={{ modalSet, lang }}>
<script define:vars={{ modalSet, lang, planetId }}>
const modalResources = document.getElementById("building-modal-req-resources");
const modalBuildings = document.getElementById("building-modal-req-buildings");
const modalResearch = document.getElementById("building-modal-req-research");
@ -202,7 +216,7 @@ const buildingsByCategory = buildingsList.reduce((acc: { [key: string]: DBBuildi
const reqResearch = modalSet[el.parentElement.parentElement.dataset.id]?.research ?? [];
modalResources.innerHTML = reqResources.length === 0 ? "None" : reqResources.map(resource => {
return `${lang['resources'].find(r => r.id === resource.name).name}: ${resource.amount}`;
return `${lang['resources'].find(r => r.id === resource.id).name}: ${resource.amount}`;
}).join("<br />");
modalBuildings.innerHTML = reqBuildings.length === 0 ? "None" : reqBuildings.map(building => {
@ -233,21 +247,28 @@ const buildingsByCategory = buildingsList.reduce((acc: { [key: string]: DBBuildi
backgroundDiv.style.display = 'none';
});
const allButtons = document.getElementsByClassName("a-button");
const allButtons = document.getElementsByClassName("item-card-build");
for(const buildingButton of allButtons) {
buildingButton.addEventListener("click", async () => {
const id = buildingButton.id.split("_")[1];
await fetch('/api/build/createBuilding', {
const response = await fetch('/api/build/createBuilding', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
building: id
building: id,
planet: planetId
})
});
if(response.status === 200) {
window.location.reload();
} else {
alert("Failed to build building: " + JSON.stringify(await response.json()));
}
});
}
</script>

View File

@ -5,7 +5,6 @@ import { getUserByAccessToken } from '../../lib/db/users';
import { getHighestWeightedLanguage, getLocales, getName, getObj } from '../../lib/utils/langDriver';
import ResourceBar from '../../components/ResourceBar.astro';
import ItemCard from '../../components/ItemCard.astro';
import DBResearch from '../../types/DBResearch';
import { getAllResearch } from '../../lib/db/research';
const researchList = await getAllResearch();
@ -17,23 +16,13 @@ if(loggedToken === null || username === "") return Astro.redirect('/logout');
const checkUser = await getUserByAccessToken(loggedToken);
if(checkUser === null || checkUser.username !== username) return Astro.redirect('/logout');
const planetId = Astro.cookies.get('planetid')?.value ?? "";
if(planetId === "") return Astro.redirect('/logout');
const locale = await getHighestWeightedLanguage(Astro.request.headers.get('accept-language'));
const lang = await getLocales(locale);
// type DBResearchDetails = DBResearch & { level: number };
// const researchDetails: DBResearchDetails[] = [];
// researchList.forEach(element => {
// const userLevel = checkUser.research.find(x => x.id === element.id)?.level ?? 0;
// const tempResDetails: DBResearchDetails = {
// level: userLevel,
// ...element
// }
// researchDetails.push(tempResDetails);
// });
const modalSet: { [key: string]: { resources: Array<any>, research: Array<any>, buildings: Array<any> } } = {};
for(const research of researchList) {
@ -66,6 +55,7 @@ for(const research of researchList) {
<ItemCard
id={research.id}
name={getObj(lang, "research", research.id).name}
level={checkUser.research.find(x => x.id === research.id)?.level.toString() ?? "0"}
description={getObj(lang, "research", research.id).description ?? ""}
image={`/images/research/${research.id}.jpeg`}
button_type="general"
@ -187,7 +177,7 @@ for(const research of researchList) {
z-index: 101;
}
</style>
<script define:vars={{ modalSet, lang }}>
<script define:vars={{ modalSet, lang, planetId }}>
const modalResources = document.getElementById("research-modal-req-resources");
const modalBuildings = document.getElementById("research-modal-req-buildings");
const modalResearch = document.getElementById("research-modal-req-research");
@ -237,23 +227,28 @@ for(const research of researchList) {
backgroundDiv.style.display = 'none';
});
const allButtons = document.getElementsByClassName("a-button");
const allButtons = document.getElementsByClassName("item-card-build");
for(const researchButton of allButtons) {
researchButton.addEventListener("click", async () => {
const id = researchButton.id.split("_")[1];
console.log(id);
await fetch('/api/research/performResearch', {
const response = await fetch('/api/research/performResearch', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
research: id
research: id,
planetId
})
});
if(response.status === 200) {
window.location.reload();
} else {
alert("Failed to build building: " + JSON.stringify(await response.json()));
}
});
}
</script>

View File

@ -2,11 +2,12 @@
import Layout from '../layouts/Layout.astro';
import NavBar from '../components/NavBar.astro';
import { getUserByNickOrEmail, getUserWithPlanets, updateLastLogin } from '../lib/db/users';
import { getUserByNickOrEmail, updateLastLogin } from '../lib/db/users';
import config from '../../config.json';
import { verify } from 'argon2';
import locationManager from '../lib/classes/managers/LocationManager';
let error = "";
@ -25,9 +26,11 @@ if(Astro.request.method === "POST") {
Astro.redirect("/login");
}
const user = await getUserByNickOrEmail(username as string);
const userDB = await getUserByNickOrEmail(username as string);
if(user !== null && await verify(user.password, password as string)) {
if(userDB !== null && await verify(userDB.password, password as string)) {
const user = locationManager.getUser(userDB._id);
if(!user) throw new Error("User not found");
const sessionTime = config.SESSION_TIME_MINUTES * 60;
const res = await fetch(`http://localhost:4321/api/auth/generateAccessToken`, {
@ -61,18 +64,14 @@ if(Astro.request.method === "POST") {
secure: true
});
Astro.cookies.set("userid", user._id?.toString() as string, {
Astro.cookies.set("userid", user.id?.toString() as string, {
path: "/",
maxAge: sessionTime,
sameSite: "lax",
secure: true
});
const userWithPlanets = await getUserWithPlanets(user._id);
if(!userWithPlanets) return;
const planetId = userWithPlanets.planets.data[0]._id;
Astro.cookies.set("planetid", planetId.toString(), {
Astro.cookies.set("planetid", user.mainPlanet._id, {
path: "/",
maxAge: sessionTime,
sameSite: "lax",

View File

@ -1,11 +1,10 @@
import type { ObjectId } from "mongodb";
import User from "../lib/classes/User";
export default interface AccessToken {
_id?: ObjectId;
type: "A" | "X";
user: ObjectId | null;
type: string;
user: User | null;
entropy: string;
createdAt: Date;
expiresAt: Date | null;
createdFrom: null | 'loginForm';
createdFrom: string | null;
}

View File

@ -1,4 +0,0 @@
export default interface Building {
id: string;
level: number;
}

View File

@ -1,11 +0,0 @@
export default interface DBBuilding {
id: string,
category: string,
requirements: {
buildings: Array<{ id: string, level: number }>,
research: Array<{ id: string, level: number }>,
resources: Array<{ name: string, amount: number }>,
},
energy: number,
multiplier: number,
}

View File

@ -1,10 +0,0 @@
export default interface DBResearch {
id: string,
requirements: {
buildings: Array<{ id: string, level: number }>,
research: Array<{ id: string, level: number }>,
resources: Array<{ name: string, amount: number }>,
},
time: number,
multiplier: number,
}

View File

@ -1,5 +0,0 @@
export default interface DBResource {
id: string,
type: string,
icon: string
}

View File

@ -1,20 +0,0 @@
export default interface DBShip {
id: string,
capacity: {
solid: number,
liquid: number,
gas: number,
},
requirements: {
buildings: Array<{ id: string, level: number }>,
research: Array<{ id: string, level: number }>,
resources: Array<{ name: string, amount: number }>,
},
time: number,
structure: {
hitpoints: number,
defense: number,
attack: number,
},
speed: number,
}

View File

@ -1,15 +0,0 @@
import type { ObjectId } from "mongodb";
import type PlayerResource from "./PlayerResource";
import type Building from "./Building";
import type Ship from "./Ship";
import type User from "./User";
export default interface Planet {
_id: ObjectId,
owner: User,
name: string,
fields: number,
resources: Array<PlayerResource>,
buildings: Array<Building>,
ships: Array<Ship>,
}

View File

@ -1,6 +0,0 @@
import type Resource from "./Resource";
export default interface DBResource extends Resource {
lastUpdated: Date;
perHourMiningRate: number;
}

View File

@ -1,4 +0,0 @@
export default interface Research {
id: string;
level: number;
}

View File

@ -1,4 +0,0 @@
export default interface Resource {
name: string;
amount: number;
}

View File

@ -1,13 +0,0 @@
export default interface Resources {
coal: number;
iron: number;
gold: number;
water: number;
sulfuricAcid: number;
liquidNitrogen: number;
hydrogen: number;
oxygen: number;
helium3: number;
}

View File

@ -1,4 +0,0 @@
export default interface Ship {
id: string;
amount: number;
}

View File

@ -1,18 +0,0 @@
import type { ObjectId } from "mongodb";
import type Research from "./Research";
import type Planet from "./Planet";
export default interface User {
_id: ObjectId;
username: string;
email: string;
password: string;
lastLogin: Date;
research: Array<Research>;
planets: {
partial: boolean;
data: Array<Planet>;
};
createdAt: Date;
updatedAt: Date;
}

View File

@ -0,0 +1,11 @@
import { ObjectId } from "mongodb";
export default interface DBAccessToken {
_id: ObjectId;
type: string;
user: ObjectId;
entropy: string;
createdAt: Date;
expiresAt: Date;
createdFrom: string | null;
}

View File

@ -0,0 +1,15 @@
import { ObjectId } from "mongodb";
export default interface DBBuilding {
_id: ObjectId;
id: string;
category: string;
requirements: {
buildings: Array<{ id: string, level: number }>,
research: Array<{ id: string, level: number }>,
resources: Array<{ id: string, amount: number }>,
};
energy: number;
time: number;
multiplier: number;
}

11
src/types/db/DBFleet.ts Normal file
View File

@ -0,0 +1,11 @@
import { ObjectId } from "mongodb";
export default interface DBFleet {
_id: ObjectId;
source: ObjectId;
destination: ObjectId;
finished: boolean;
returning: boolean;
mission: string;
ships: Array<{ id: string, amount: number }>;
}

7
src/types/db/DBGalaxy.ts Normal file
View File

@ -0,0 +1,7 @@
import { ObjectId } from "mongodb";
export default interface DBGalaxy {
_id: ObjectId;
name: string;
sectors: Array<ObjectId>;
}

10
src/types/db/DBPlanet.ts Normal file
View File

@ -0,0 +1,10 @@
import { ObjectId } from "mongodb";
export default interface DBPlanet {
_id: ObjectId;
name: string;
fields: number;
resources: Array<{ id: string, amount: number, lastUpdated: Date, perHourMiningRate: number }>;
buildings: Array<{ id: string, level: number }>;
ships: Array<{ id: string, amount: number }>;
}

View File

@ -0,0 +1,13 @@
import { ObjectId } from "mongodb";
export default interface DBResearch {
_id: ObjectId;
id: string;
requirements: {
buildings: Array<{ id: string, level: number }>,
research: Array<{ id: string, level: number }>,
resources: Array<{ id: string, amount: number }>,
};
time: number;
multiplier: number;
}

View File

@ -0,0 +1,8 @@
import { ObjectId } from "mongodb";
export default interface DBResource {
_id: ObjectId;
id: string;
type: string;
icon: string;
}

8
src/types/db/DBSector.ts Normal file
View File

@ -0,0 +1,8 @@
import { ObjectId } from "mongodb";
export default interface DBSector {
_id: ObjectId;
name: string;
expedition: any; // TODO: Define expedition type
systems: Array<ObjectId>;
}

23
src/types/db/DBShip.ts Normal file
View File

@ -0,0 +1,23 @@
import { ObjectId } from "mongodb";
export default interface DBShip {
_id: ObjectId;
id: string;
capacity: {
solid: number;
liquid: number;
gas: number;
};
requirements: {
buildings: Array<{ id: string, level: number }>,
research: Array<{ id: string, level: number }>,
resources: Array<{ id: string, amount: number }>,
};
time: number;
structure: {
hitpoints: number;
defense: number;
attack: number;
};
speed: number;
}

8
src/types/db/DBSystem.ts Normal file
View File

@ -0,0 +1,8 @@
import { ObjectId } from "mongodb";
export default interface DBSystem {
_id: ObjectId;
name: string;
ownedBy: ObjectId;
planets: Array<ObjectId>;
}

13
src/types/db/DBUser.ts Normal file
View File

@ -0,0 +1,13 @@
import { ObjectId } from "mongodb";
export default interface DBUser {
_id: ObjectId;
username: string;
email: string;
password: string;
lastLogin: Date;
createdAt: Date;
updatedAt: Date;
research: Array<{ id: string, level: number }>;
mainPlanet: ObjectId;
}