├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ └── nodejs-ci.yml ├── .gitignore ├── .vscode └── launch.json ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── client ├── .browserslistrc ├── .dockerignore ├── .editorconfig ├── .env.example ├── Dockerfile ├── README.md ├── index.html ├── package.json ├── public │ ├── assets │ │ ├── LICENSE │ │ ├── css │ │ │ ├── app.min.css │ │ │ ├── fonts │ │ │ │ ├── bootstrap-icons.woff │ │ │ │ └── bootstrap-icons.woff2 │ │ │ ├── images │ │ │ │ ├── cover.jpg │ │ │ │ ├── logo.png │ │ │ │ └── pattern.png │ │ │ └── vendor.min.css │ │ ├── js │ │ │ ├── app.min.js │ │ │ └── vendor.min.js │ │ └── webfonts │ │ │ ├── all.min.css │ │ │ ├── fa-brands-400.ttf │ │ │ ├── fa-brands-400.woff2 │ │ │ ├── fa-regular-400.ttf │ │ │ ├── fa-regular-400.woff2 │ │ │ ├── fa-solid-900.ttf │ │ │ ├── fa-solid-900.woff2 │ │ │ ├── fa-v4compatibility.ttf │ │ │ └── fa-v4compatibility.woff2 │ ├── favicon.ico │ ├── favicon_dark.ico │ ├── favicon_touch.png │ ├── solaris_icon-android.png │ ├── solaris_icon-ipad-r.png │ ├── solaris_icon-ipad.png │ └── solaris_icon-iphone.png ├── src │ ├── App.vue │ ├── assets │ │ ├── audio │ │ │ ├── backspace.mp3 │ │ │ ├── click.mp3 │ │ │ ├── close.mp3 │ │ │ ├── dialog_open.mp3 │ │ │ ├── download.mp3 │ │ │ ├── hover.mp3 │ │ │ ├── join.mp3 │ │ │ ├── leave.mp3 │ │ │ ├── loading.mp3 │ │ │ ├── open.mp3 │ │ │ ├── quit.mp3 │ │ │ └── type.mp3 │ │ ├── avatars │ │ │ ├── 0.png │ │ │ ├── 1.png │ │ │ ├── 10.png │ │ │ ├── 11.png │ │ │ ├── 12.png │ │ │ ├── 13.png │ │ │ ├── 14.png │ │ │ ├── 15.png │ │ │ ├── 16.png │ │ │ ├── 17.png │ │ │ ├── 18.png │ │ │ ├── 19.png │ │ │ ├── 2.png │ │ │ ├── 20.png │ │ │ ├── 21.png │ │ │ ├── 22.png │ │ │ ├── 23.png │ │ │ ├── 24.png │ │ │ ├── 25.png │ │ │ ├── 26.png │ │ │ ├── 27.png │ │ │ ├── 28.png │ │ │ ├── 29.png │ │ │ ├── 3.png │ │ │ ├── 30.png │ │ │ ├── 31.png │ │ │ ├── 32.png │ │ │ ├── 33.png │ │ │ ├── 34.png │ │ │ ├── 35.png │ │ │ ├── 36.png │ │ │ ├── 37.png │ │ │ ├── 38.png │ │ │ ├── 39.png │ │ │ ├── 4.png │ │ │ ├── 40.png │ │ │ ├── 41.png │ │ │ ├── 42.png │ │ │ ├── 43.png │ │ │ ├── 5.png │ │ │ ├── 6.png │ │ │ ├── 7.png │ │ │ ├── 8.png │ │ │ ├── 9.png │ │ │ ├── ape.png │ │ │ ├── fab.gif │ │ │ └── red.png │ │ ├── badges │ │ │ ├── LICENSE │ │ │ ├── ally.png │ │ │ ├── dauntless.png │ │ │ ├── diplomat.png │ │ │ ├── enemy.png │ │ │ ├── roleplay.png │ │ │ ├── sleepless.png │ │ │ ├── special_anonymous.png │ │ │ ├── special_arcade.png │ │ │ ├── special_battleRoyale.png │ │ │ ├── special_dark.png │ │ │ ├── special_fog.png │ │ │ ├── special_freeForAll.png │ │ │ ├── special_homeStar.png │ │ │ ├── special_homeStarElimination.png │ │ │ ├── special_kingOfTheHill.png │ │ │ ├── special_orbital.png │ │ │ ├── special_tinyGalaxy.png │ │ │ ├── special_ultraDark.png │ │ │ ├── strategist.png │ │ │ └── victor32.png │ │ ├── fonts │ │ │ ├── ChakraPetch-Medium.ttf │ │ │ ├── ChakraPetch-Regular.ttf │ │ │ └── OFL.txt │ │ ├── levels │ │ │ ├── 1.png │ │ │ ├── 10.png │ │ │ ├── 2.png │ │ │ ├── 3.png │ │ │ ├── 4.png │ │ │ ├── 5.png │ │ │ ├── 6.png │ │ │ ├── 7.png │ │ │ ├── 8.png │ │ │ └── 9.png │ │ ├── logo.png │ │ ├── map-objects-symbols │ │ │ ├── 128x128_star_black_hole.svg │ │ │ ├── 128x128_star_pulsar.svg │ │ │ ├── 128x128_star_scannable.svg │ │ │ ├── 128x128_star_scannable_binary.svg │ │ │ ├── neb0-starless-bright.png │ │ │ └── vortex.png │ │ ├── map-objects │ │ │ ├── 128x128_carrier.svg │ │ │ ├── 128x128_circle_carrier.svg │ │ │ ├── 128x128_diamond_carrier.svg │ │ │ ├── 128x128_hexagon_carrier.svg │ │ │ ├── 128x128_square_carrier.svg │ │ │ ├── 128x128_star_black_hole.svg │ │ │ ├── 128x128_star_black_hole_binary.svg │ │ │ ├── 128x128_star_home.svg │ │ │ ├── 128x128_star_scannable.svg │ │ │ ├── 128x128_star_scannable_binary.svg │ │ │ ├── 128x128_star_unscannable.svg │ │ │ ├── 128x128_star_unscannable_binary.svg │ │ │ ├── 256x256_circle.svg │ │ │ ├── 256x256_circle_partial.svg │ │ │ ├── 256x256_circle_partial_warp_gate.svg │ │ │ ├── 256x256_circle_warp_gate.svg │ │ │ ├── 256x256_diamond.svg │ │ │ ├── 256x256_diamond_partial.svg │ │ │ ├── 256x256_diamond_partial_warp_gate.svg │ │ │ ├── 256x256_diamond_warp_gate.svg │ │ │ ├── 256x256_hexagon.svg │ │ │ ├── 256x256_hexagon_partial.svg │ │ │ ├── 256x256_hexagon_partial_warp_gate.svg │ │ │ ├── 256x256_hexagon_warp_gate.svg │ │ │ ├── 256x256_square.svg │ │ │ ├── 256x256_square_partial.svg │ │ │ ├── 256x256_square_partial_warp_gate.svg │ │ │ └── 256x256_square_warp_gate.svg │ │ ├── nebula │ │ │ ├── neb0-starless-bright.png │ │ │ ├── neb0-starless.png │ │ │ ├── neb1-starless-bright.png │ │ │ ├── neb1-starless.png │ │ │ ├── star-nebula-0.png │ │ │ ├── star-nebula-1.png │ │ │ └── star-nebula-2.png │ │ ├── parallax │ │ │ ├── layer1.png │ │ │ ├── layer2.png │ │ │ └── layer3.png │ │ ├── screenshots │ │ │ ├── 16_player_relaxed.png │ │ │ ├── 1v1_rt.png │ │ │ ├── 1v1_tb.png │ │ │ ├── 32_player.png │ │ │ ├── codex │ │ │ │ ├── carrier1.png │ │ │ │ ├── carrier2.png │ │ │ │ ├── eventlog1.png │ │ │ │ ├── map1.png │ │ │ │ ├── research1.png │ │ │ │ ├── star1.png │ │ │ │ ├── star2.png │ │ │ │ ├── star3.png │ │ │ │ ├── star4.png │ │ │ │ └── star5.png │ │ │ ├── featured.png │ │ │ ├── game-carousel-1.png │ │ │ ├── game-carousel-2.png │ │ │ ├── game-carousel-3.png │ │ │ ├── game1.png │ │ │ ├── home-1.png │ │ │ ├── home-2.png │ │ │ ├── home-3.png │ │ │ ├── home-4.png │ │ │ ├── home-5.png │ │ │ ├── new_player_rt.png │ │ │ ├── special_anonymous.png │ │ │ ├── special_arcade.png │ │ │ ├── special_battleRoyale.png │ │ │ ├── special_dark.png │ │ │ ├── special_fog.png │ │ │ ├── special_freeForAll.png │ │ │ ├── special_homeStar.png │ │ │ ├── special_homeStarElimination.png │ │ │ ├── special_kingOfTheHill.png │ │ │ ├── special_orbital.png │ │ │ ├── special_tinyGalaxy.png │ │ │ ├── special_ultraDark.png │ │ │ ├── standard_rt.png │ │ │ ├── standard_tb.png │ │ │ └── tutorial.png │ │ ├── solaris_logo_text_1000w.png │ │ ├── specialists │ │ │ ├── afterburn.svg │ │ │ ├── airtight-hatch.svg │ │ │ ├── alien-bug.svg │ │ │ ├── alien-egg.svg │ │ │ ├── alien-stare.svg │ │ │ ├── android-mask.svg │ │ │ ├── astronaut-helmet.svg │ │ │ ├── autogun.svg │ │ │ ├── bolter-gun.svg │ │ │ ├── bubble-field.svg │ │ │ ├── cannister.svg │ │ │ ├── cryo-chamber.svg │ │ │ ├── cyborg-face.svg │ │ │ ├── defense-satellite.svg │ │ │ ├── double-ringed-orb.svg │ │ │ ├── eclipse.svg │ │ │ ├── energise.svg │ │ │ ├── energy-tank.svg │ │ │ ├── forward-field.svg │ │ │ ├── habitat-dome.svg │ │ │ ├── hazmat-suit.svg │ │ │ ├── jet-fighter.svg │ │ │ ├── jetpack.svg │ │ │ ├── klingon.svg │ │ │ ├── laser-blast.svg │ │ │ ├── laser-turret.svg │ │ │ ├── license.txt │ │ │ ├── light-sabers.svg │ │ │ ├── lunar-module.svg │ │ │ ├── mecha-head.svg │ │ │ ├── mecha-mask.svg │ │ │ ├── mechanical-arm.svg │ │ │ ├── megabot.svg │ │ │ ├── metal-scales.svg │ │ │ ├── metroid.svg │ │ │ ├── missile-mech.svg │ │ │ ├── missile-pod.svg │ │ │ ├── missile-swarm.svg │ │ │ ├── mono-wheel-robot.svg │ │ │ ├── observatory.svg │ │ │ ├── pirate.svg │ │ │ ├── power-generator.svg │ │ │ ├── radar-dish.svg │ │ │ ├── ray-gun.svg │ │ │ ├── ringed-planet.svg │ │ │ ├── robot-golem.svg │ │ │ ├── robot-leg.svg │ │ │ ├── rocket-flight.svg │ │ │ ├── rocket-thruster.svg │ │ │ ├── rocket.svg │ │ │ ├── round-silo.svg │ │ │ ├── sattelite.svg │ │ │ ├── sentry-gun.svg │ │ │ ├── sinusoidal-beam.svg │ │ │ ├── solar-system.svg │ │ │ ├── space-suit.svg │ │ │ ├── spaceship.svg │ │ │ ├── spoutnik.svg │ │ │ ├── star-gate.svg │ │ │ ├── starfighter.svg │ │ │ ├── strafe.svg │ │ │ ├── targeting.svg │ │ │ ├── techno-heart.svg │ │ │ ├── tesla-turret.svg │ │ │ ├── ufo.svg │ │ │ ├── vintage-robot.svg │ │ │ ├── walking-scout.svg │ │ │ └── zat-gun.svg │ │ ├── stars │ │ │ ├── 128x128_star_pulsar.svg │ │ │ ├── star-asteroid-field-0.png │ │ │ ├── star-asteroid-field-1.png │ │ │ ├── star-asteroid-field-2.png │ │ │ ├── star.png │ │ │ └── vortex.png │ │ └── styles.css │ ├── clientEventBus.ts │ ├── config │ │ └── gamesettings │ │ │ ├── 16player_realTime.json │ │ │ ├── 16player_turnBased.json │ │ │ ├── 1v1.json │ │ │ ├── 1v1turnBased.json │ │ │ ├── 32player_normal.json │ │ │ ├── 32player_ultradark.json │ │ │ ├── newPlayer.json │ │ │ ├── special_anonymous.json │ │ │ ├── special_arcade.json │ │ │ ├── special_battleRoyale.json │ │ │ ├── special_dark.json │ │ │ ├── special_fog.json │ │ │ ├── special_freeForAll.json │ │ │ ├── special_homeStar.json │ │ │ ├── special_homeStarElimination.json │ │ │ ├── special_kingOfTheHill.json │ │ │ ├── special_orbital.json │ │ │ ├── special_tinyGalaxy.json │ │ │ ├── special_ultraDark.json │ │ │ ├── standard.json │ │ │ └── turnBased.json │ ├── eventBus.ts │ ├── eventBusEventNames │ │ ├── diplomacy.ts │ │ ├── eventBusEventName.ts │ │ ├── game.ts │ │ ├── gameCommand.ts │ │ ├── map.ts │ │ ├── mapCommand.ts │ │ ├── menu.ts │ │ ├── player.ts │ │ └── user.ts │ ├── game │ │ ├── PathManager.ts │ │ ├── animation.ts │ │ ├── audio.ts │ │ ├── background.ts │ │ ├── carrier.ts │ │ ├── chunks.ts │ │ ├── container.ts │ │ ├── debugTools.ts │ │ ├── eventEmitter.ts │ │ ├── helpers.ts │ │ ├── highlight.ts │ │ ├── map.ts │ │ ├── mapObject.ts │ │ ├── orbital.ts │ │ ├── playerNames.ts │ │ ├── rulerPoints.ts │ │ ├── screenshot.ts │ │ ├── star.ts │ │ ├── territories.ts │ │ ├── texture.ts │ │ ├── tooltip.ts │ │ ├── waypoints.ts │ │ └── wormHole.ts │ ├── integration │ │ ├── messages.ts │ │ └── solaris.ts │ ├── main.ts │ ├── mutationNames │ │ ├── gameMutationNames.ts │ │ └── playerMutationNames.ts │ ├── router.js │ ├── services │ │ ├── api │ │ │ ├── admin.js │ │ │ ├── announcements.js │ │ │ ├── auth.js │ │ │ ├── base.js │ │ │ ├── carrier.js │ │ │ ├── colour.js │ │ │ ├── conversation.js │ │ │ ├── diplomacy.js │ │ │ ├── event.js │ │ │ ├── game.js │ │ │ ├── guild.js │ │ │ ├── ledger.js │ │ │ ├── report.js │ │ │ ├── research.js │ │ │ ├── shop.js │ │ │ ├── specialist.js │ │ │ ├── star.js │ │ │ └── trade.js │ │ ├── data │ │ │ ├── keyboardShortcuts.js │ │ │ ├── menuStates.js │ │ │ └── sortInfo.js │ │ ├── diplomacyHelper.js │ │ ├── gameHelper.ts │ │ ├── gridHelper.js │ │ ├── mentionHelper.js │ │ ├── randomHelper.js │ │ ├── technologyHelper.js │ │ ├── typedapi │ │ │ ├── admin.ts │ │ │ ├── badge.ts │ │ │ ├── index.ts │ │ │ ├── spectator.ts │ │ │ └── user.ts │ │ └── waypointHelper.js │ ├── sockets │ │ ├── socketEmitters │ │ │ ├── clientSocketEmitter.ts │ │ │ ├── player.ts │ │ │ └── user.ts │ │ ├── socketEventNames │ │ │ └── client.ts │ │ └── socketHandlers │ │ │ ├── clientHandler.ts │ │ │ ├── clientSocketHandler.ts │ │ │ ├── diplomacy.ts │ │ │ ├── game.ts │ │ │ ├── player.ts │ │ │ └── user.ts │ ├── store.ts │ ├── types │ │ ├── combat.ts │ │ ├── game.ts │ │ ├── ruler.ts │ │ ├── waypoint.ts │ │ └── window.ts │ ├── util │ │ ├── confirm.ts │ │ ├── eventDeduplication.js │ │ ├── format.ts │ │ ├── http.ts │ │ ├── keys.ts │ │ ├── markdown.js │ │ ├── messages.ts │ │ └── reactiveHooks.ts │ ├── views │ │ ├── Announcements.vue │ │ ├── CommunityGuidelines.vue │ │ ├── Home.vue │ │ ├── MainMenu.vue │ │ ├── PrivacyPolicy.vue │ │ ├── account │ │ │ ├── Achievements.vue │ │ │ ├── Creation.vue │ │ │ ├── ExternalResetPassword.vue │ │ │ ├── ForgotPassword.vue │ │ │ ├── ForgotUsername.vue │ │ │ ├── ResetEmail.vue │ │ │ ├── ResetPassword.vue │ │ │ ├── ResetUsername.vue │ │ │ ├── Settings.vue │ │ │ ├── Warnings.vue │ │ │ └── components │ │ │ │ ├── AchievementStats.vue │ │ │ │ ├── Login.vue │ │ │ │ └── Notifications.vue │ │ ├── admin │ │ │ ├── AdministrationPage.vue │ │ │ ├── Announcements.vue │ │ │ ├── Games.vue │ │ │ ├── Insights.vue │ │ │ ├── PasswordResets.vue │ │ │ ├── Reports.vue │ │ │ ├── Users.vue │ │ │ └── components │ │ │ │ ├── AddWarning.vue │ │ │ │ ├── CreateAnnouncement.vue │ │ │ │ └── MessageReport.vue │ │ ├── components │ │ │ ├── Announcement.vue │ │ │ ├── AnnouncementsButton.vue │ │ │ ├── FormErrorList.vue │ │ │ ├── HelpTooltip.vue │ │ │ ├── LatestAnnouncement.vue │ │ │ ├── LoadingSpinner.vue │ │ │ ├── Logo.vue │ │ │ ├── Parallax.vue │ │ │ ├── Poll.vue │ │ │ ├── SvgWrapper.vue │ │ │ ├── ViewCollapsePanel.vue │ │ │ ├── ViewContainer.vue │ │ │ ├── ViewContainerTopBar.vue │ │ │ ├── ViewSubtitle.vue │ │ │ ├── ViewTitle.vue │ │ │ └── modal │ │ │ │ ├── ConfirmationDialog.vue │ │ │ │ ├── DialogModal.vue │ │ │ │ └── ModalButton.vue │ │ ├── game │ │ │ ├── ActiveGames.vue │ │ │ ├── Create.vue │ │ │ ├── Detail.vue │ │ │ ├── Game.vue │ │ │ ├── GameControl.vue │ │ │ ├── GamePlayerControl.vue │ │ │ ├── Leaderboard.vue │ │ │ ├── List.vue │ │ │ └── components │ │ │ │ ├── CountdownTimer.vue │ │ │ │ ├── GameContainer.vue │ │ │ │ ├── MenuTitle.vue │ │ │ │ ├── badges │ │ │ │ ├── Badge.vue │ │ │ │ ├── BadgeShopList.vue │ │ │ │ ├── PlayerBadgeShop.vue │ │ │ │ ├── PlayerBadges.vue │ │ │ │ └── UserBadges.vue │ │ │ │ ├── carrier │ │ │ │ ├── BuildCarrier.vue │ │ │ │ ├── CarrierDetail.vue │ │ │ │ ├── CarrierLabel.vue │ │ │ │ ├── CarrierRename.vue │ │ │ │ ├── CarrierSpecialist.vue │ │ │ │ ├── CarrierWaypoint.vue │ │ │ │ ├── CarrierWaypoints.vue │ │ │ │ ├── CombatCalculator.vue │ │ │ │ ├── GiftCarrier.vue │ │ │ │ ├── ShipTransfer.vue │ │ │ │ ├── WaypointRow.vue │ │ │ │ └── WaypointTable.vue │ │ │ │ ├── diplomacy │ │ │ │ ├── Diplomacy.vue │ │ │ │ ├── DiplomacyIcons.vue │ │ │ │ └── DiplomacyRow.vue │ │ │ │ ├── eventLog │ │ │ │ ├── EventLog.vue │ │ │ │ └── events │ │ │ │ │ ├── CombatActorDescription.vue │ │ │ │ │ ├── CombatEventSide.vue │ │ │ │ │ ├── EventsList.vue │ │ │ │ │ ├── EventsListItem.vue │ │ │ │ │ ├── GameDiplomacyPeaceDeclared.vue │ │ │ │ │ ├── GameDiplomacyWarDeclared.vue │ │ │ │ │ ├── GameEnded.vue │ │ │ │ │ ├── GamePaused.vue │ │ │ │ │ ├── GamePlayerAFK.vue │ │ │ │ │ ├── GamePlayerBadgePurchased.vue │ │ │ │ │ ├── GamePlayerDefeated.vue │ │ │ │ │ ├── GamePlayerJoined.vue │ │ │ │ │ ├── GamePlayerQuit.vue │ │ │ │ │ ├── GameStarted.vue │ │ │ │ │ ├── PlayerBulkInfrastructureUpgraded.vue │ │ │ │ │ ├── PlayerCarrierSpecialistHired.vue │ │ │ │ │ ├── PlayerCombatCarrierEvent.vue │ │ │ │ │ ├── PlayerCombatStarEvent.vue │ │ │ │ │ ├── PlayerConversationCreated.vue │ │ │ │ │ ├── PlayerConversationInvited.vue │ │ │ │ │ ├── PlayerConversationLeft.vue │ │ │ │ │ ├── PlayerCreditsReceived.vue │ │ │ │ │ ├── PlayerCreditsSent.vue │ │ │ │ │ ├── PlayerCreditsSpecialistsReceived.vue │ │ │ │ │ ├── PlayerCreditsSpecialistsSent.vue │ │ │ │ │ ├── PlayerDebtForgiven.vue │ │ │ │ │ ├── PlayerDebtSettled.vue │ │ │ │ │ ├── PlayerDiplomacyStatusChanged.vue │ │ │ │ │ ├── PlayerGalacticCycleCompleteEvent.vue │ │ │ │ │ ├── PlayerGiftReceived.vue │ │ │ │ │ ├── PlayerGiftSent.vue │ │ │ │ │ ├── PlayerRenownReceived.vue │ │ │ │ │ ├── PlayerRenownSent.vue │ │ │ │ │ ├── PlayerResearchComplete.vue │ │ │ │ │ ├── PlayerStarAbandoned.vue │ │ │ │ │ ├── PlayerStarDied.vue │ │ │ │ │ ├── PlayerStarReignited.vue │ │ │ │ │ ├── PlayerStarSpecialistHired.vue │ │ │ │ │ ├── PlayerTechnologyReceived.vue │ │ │ │ │ └── PlayerTechnologySent.vue │ │ │ │ ├── galaxy │ │ │ │ ├── CapitalRow.vue │ │ │ │ ├── CapitalsTable.vue │ │ │ │ ├── CarrierRow.vue │ │ │ │ ├── CarriersTable.vue │ │ │ │ ├── EmpireRow.vue │ │ │ │ ├── EmpiresTable.vue │ │ │ │ ├── Galaxy.vue │ │ │ │ ├── NaturalResourcesRow.vue │ │ │ │ ├── NaturalResourcesTable.vue │ │ │ │ ├── ShipRow.vue │ │ │ │ ├── ShipsTable.vue │ │ │ │ ├── StarRow.vue │ │ │ │ ├── StarTypesRow.vue │ │ │ │ ├── StarTypesTable.vue │ │ │ │ ├── StarsTable.vue │ │ │ │ ├── TechnologyRow.vue │ │ │ │ └── TechnologyTable.vue │ │ │ │ ├── gameList │ │ │ │ ├── ActiveGames.vue │ │ │ │ ├── CompletedGames.vue │ │ │ │ ├── OpenGames.vue │ │ │ │ └── SpectatingGames.vue │ │ │ │ ├── inbox │ │ │ │ ├── Chat.vue │ │ │ │ ├── Inbox.vue │ │ │ │ └── conversations │ │ │ │ │ ├── ConversationCompose.vue │ │ │ │ │ ├── ConversationCreate.vue │ │ │ │ │ ├── ConversationDetail.vue │ │ │ │ │ ├── ConversationList.vue │ │ │ │ │ ├── ConversationMessage.vue │ │ │ │ │ ├── ConversationMessageContextMenu.vue │ │ │ │ │ ├── ConversationMessagePin.vue │ │ │ │ │ ├── ConversationParticipants.vue │ │ │ │ │ ├── ConversationPreview.vue │ │ │ │ │ └── ConversationTradeEvent.vue │ │ │ │ ├── intel │ │ │ │ ├── Intel.vue │ │ │ │ ├── LineChart.vue │ │ │ │ ├── PieChart.vue │ │ │ │ └── PolarAreaChart.vue │ │ │ │ ├── leaderboard │ │ │ │ ├── ConcedeDefeatButton.vue │ │ │ │ ├── Leaderboard.vue │ │ │ │ ├── LeaderboardRow.vue │ │ │ │ ├── PlayerLeaderboard.vue │ │ │ │ └── TeamLeaderboard.vue │ │ │ │ ├── ledger │ │ │ │ ├── Ledger.vue │ │ │ │ ├── LedgerRow.vue │ │ │ │ └── LedgerTable.vue │ │ │ │ ├── menu │ │ │ │ ├── CommunityGuidelinesBar.vue │ │ │ │ ├── ErrorLog.vue │ │ │ │ ├── FluxBar.vue │ │ │ │ ├── FooterBar.vue │ │ │ │ ├── HamburgerMenu.vue │ │ │ │ ├── HeaderBar.vue │ │ │ │ ├── LeaderboardGuildTable.vue │ │ │ │ ├── LeaderboardUserEloTable.vue │ │ │ │ ├── LeaderboardUserTable.vue │ │ │ │ ├── LockedGameOverlay.vue │ │ │ │ ├── MainBar.vue │ │ │ │ ├── MapObjectSelector.vue │ │ │ │ ├── NotLoggedInBar.vue │ │ │ │ ├── Options.vue │ │ │ │ ├── OptionsForm.vue │ │ │ │ ├── PlayerAvatar.vue │ │ │ │ ├── PlayerList.vue │ │ │ │ ├── ReadyStatusButton.vue │ │ │ │ ├── ResearchProgress.vue │ │ │ │ ├── ServerConnectionStatus.vue │ │ │ │ ├── SidebarMenu.vue │ │ │ │ ├── SidebarMenuItem.vue │ │ │ │ ├── SortableLeaderboard.vue │ │ │ │ ├── SpectatingWarningBar.vue │ │ │ │ ├── TickSelector.vue │ │ │ │ └── TutorialGame.vue │ │ │ │ ├── notes │ │ │ │ └── GameNotes.vue │ │ │ │ ├── player │ │ │ │ ├── Achievements.vue │ │ │ │ ├── ColourOverrideDialog.vue │ │ │ │ ├── EloRating.vue │ │ │ │ ├── Overview.vue │ │ │ │ ├── Player.vue │ │ │ │ ├── PlayerDiplomaticStatusIcon.vue │ │ │ │ ├── PlayerIcon.vue │ │ │ │ ├── PlayerIconShape.vue │ │ │ │ ├── PlayerMissedTurns.vue │ │ │ │ ├── PlayerOnlineStatus.vue │ │ │ │ ├── PlayerOpenSlotStatus.vue │ │ │ │ ├── PlayerReport.vue │ │ │ │ ├── PlayerTitle.vue │ │ │ │ ├── PlayerTrade.vue │ │ │ │ ├── Reputation.vue │ │ │ │ ├── Research.vue │ │ │ │ ├── ResearchRow.vue │ │ │ │ ├── Roles.vue │ │ │ │ ├── SendCredits.vue │ │ │ │ ├── SendCreditsSpecialists.vue │ │ │ │ ├── SendRenown.vue │ │ │ │ ├── SendTechnology.vue │ │ │ │ ├── StatisticRow.vue │ │ │ │ ├── Statistics.vue │ │ │ │ ├── Trade.vue │ │ │ │ ├── TradeHistory.vue │ │ │ │ └── YourInfrastructure.vue │ │ │ │ ├── report │ │ │ │ └── ReportPlayer.vue │ │ │ │ ├── research │ │ │ │ ├── ProgressSummary.vue │ │ │ │ ├── Research.vue │ │ │ │ └── Selection.vue │ │ │ │ ├── ruler │ │ │ │ └── Ruler.vue │ │ │ │ ├── settings │ │ │ │ ├── GameSettingValue.vue │ │ │ │ ├── GameSettings.vue │ │ │ │ └── Settings.vue │ │ │ │ ├── shared │ │ │ │ ├── Infrastructure.vue │ │ │ │ ├── MentionBox.vue │ │ │ │ ├── OrbitalMechanicsETAWarning.vue │ │ │ │ └── TeamName.vue │ │ │ │ ├── specialist │ │ │ │ ├── HireSpecialistCarrier.vue │ │ │ │ ├── HireSpecialistStar.vue │ │ │ │ ├── SpecialistBanList.vue │ │ │ │ ├── SpecialistBanListSelection.vue │ │ │ │ ├── SpecialistBanListTable.vue │ │ │ │ └── SpecialistIcon.vue │ │ │ │ ├── spectators │ │ │ │ ├── InviteSpectator.vue │ │ │ │ ├── Spectator.vue │ │ │ │ └── Spectators.vue │ │ │ │ ├── star │ │ │ │ ├── BulkInfrastructureUpgrade.vue │ │ │ │ ├── BulkInfrastructureUpgradeScheduleTable.vue │ │ │ │ ├── BulkInfrastructureUpgradeScheduleTableRow.vue │ │ │ │ ├── BulkInfrastructureUpgradeStarTable.vue │ │ │ │ ├── BulkInfrastructureUpgradeStarTableRow.vue │ │ │ │ ├── IgnoreBulkUpgrade.vue │ │ │ │ ├── InfrastructureUpgrade.vue │ │ │ │ ├── InfrastructureUpgradeCompact.vue │ │ │ │ ├── RepeatBulkUpgrade.vue │ │ │ │ ├── StarDetail.vue │ │ │ │ ├── StarIcon.vue │ │ │ │ ├── StarLabel.vue │ │ │ │ ├── StarResources.vue │ │ │ │ ├── StarSpecialist.vue │ │ │ │ └── TrashBulkUpgrade.vue │ │ │ │ ├── tutorial │ │ │ │ ├── Tutorial.vue │ │ │ │ ├── TutorialCombatBasics.vue │ │ │ │ ├── TutorialFleetMovement.vue │ │ │ │ ├── TutorialInfrastructureAndExpansion.vue │ │ │ │ ├── TutorialOriginal.vue │ │ │ │ ├── TutorialScienceAndResearch.vue │ │ │ │ ├── TutorialSpecialStarTypes.vue │ │ │ │ ├── TutorialStarsAndCarriers.vue │ │ │ │ └── tutorialMixin.js │ │ │ │ └── welcome │ │ │ │ ├── EnterPassword.vue │ │ │ │ ├── NewPlayerMessage.vue │ │ │ │ ├── SelectAlias.vue │ │ │ │ ├── SelectAvatar.vue │ │ │ │ ├── SelectColour.vue │ │ │ │ ├── ShareLink.vue │ │ │ │ └── Welcome.vue │ │ ├── guild │ │ │ ├── Create.vue │ │ │ ├── Detail.vue │ │ │ ├── MyGuild.vue │ │ │ ├── Rename.vue │ │ │ └── components │ │ │ │ ├── Application.vue │ │ │ │ ├── Invite.vue │ │ │ │ ├── Member.vue │ │ │ │ ├── MemberList.vue │ │ │ │ ├── NewInvite.vue │ │ │ │ └── UserGuildInfo.vue │ │ └── shop │ │ │ ├── Avatars.vue │ │ │ ├── GalacticCredits.vue │ │ │ ├── PurchaseComplete.vue │ │ │ └── PurchaseFailed.vue │ ├── vite-env.d.ts │ └── voronoi │ │ └── Javascript-Voronoi │ │ ├── CHANGELOG.md │ │ ├── CREDITS.md │ │ ├── LICENSE.md │ │ ├── README.md │ │ ├── excanvas │ │ ├── README │ │ ├── excanvas.compiled.js │ │ └── excanvas.js │ │ ├── mootools │ │ ├── mootools-core-1.3.2.js │ │ └── mootools-more-1.3.2.1.js │ │ ├── package.json │ │ ├── rhill-voronoi-core.js │ │ ├── rhill-voronoi-core.min.js │ │ ├── rhill-voronoi-demo1.html │ │ ├── rhill-voronoi-demo2.html │ │ ├── rhill-voronoi-demo3.php │ │ ├── rhill-voronoi-demo4.html │ │ ├── rhill-voronoi-demo5.html │ │ └── rhill-voronoi.html ├── tsconfig.json └── vite.config.js ├── common ├── package.json ├── src │ ├── api │ │ ├── controllers │ │ │ ├── admin.ts │ │ │ ├── badge.ts │ │ │ ├── index.ts │ │ │ ├── spectator.ts │ │ │ └── user.ts │ │ └── types │ │ │ └── common │ │ │ ├── announcement.ts │ │ │ ├── avatar.ts │ │ │ ├── badge.ts │ │ │ ├── carrier.ts │ │ │ ├── carrierWaypoint.ts │ │ │ ├── combat.ts │ │ │ ├── conversation.ts │ │ │ ├── conversationMessage.ts │ │ │ ├── diplomacy.ts │ │ │ ├── events │ │ │ └── event.ts │ │ │ ├── game.ts │ │ │ ├── guild.ts │ │ │ ├── infrastructureUpgrade.ts │ │ │ ├── leaderboard.ts │ │ │ ├── ledger.ts │ │ │ ├── location.ts │ │ │ ├── map.ts │ │ │ ├── player.ts │ │ │ ├── settings.ts │ │ │ ├── specialist.ts │ │ │ ├── star.ts │ │ │ ├── subscriptions.ts │ │ │ ├── trade.ts │ │ │ └── user.ts │ ├── events │ │ ├── emitter.ts │ │ ├── handler.ts │ │ └── index.ts │ ├── index.ts │ ├── sockets │ │ └── socketEventNames │ │ │ ├── diplomacy.ts │ │ │ ├── game.ts │ │ │ ├── player.ts │ │ │ └── user.ts │ └── utilities │ │ └── cast.ts └── tsconfig.json ├── docker-compose.requirements.yml ├── docker-compose.yaml ├── package-lock.json ├── package.json ├── pm2.config.js └── server ├── .dockerignore ├── .env.example ├── .env_override_docker ├── Dockerfile ├── api ├── controllers │ ├── admin.ts │ ├── announcement.ts │ ├── auth.ts │ ├── badges.ts │ ├── carrier.ts │ ├── colour.ts │ ├── conversation.ts │ ├── diplomacy.ts │ ├── event.ts │ ├── game.ts │ ├── guild.ts │ ├── ledger.ts │ ├── report.ts │ ├── research.ts │ ├── shop.ts │ ├── specialist.ts │ ├── spectator.ts │ ├── star.ts │ ├── trade.ts │ └── user.ts ├── express.ts ├── index.ts ├── middleware │ ├── auth.ts │ ├── core.ts │ ├── game.ts │ ├── index.ts │ ├── player.ts │ └── playerMutex.ts ├── requests │ ├── admin.ts │ ├── announcements.ts │ ├── auth.ts │ ├── badges.ts │ ├── carrier.ts │ ├── colour.ts │ ├── conversation.ts │ ├── game.ts │ ├── guild.ts │ ├── helpers.ts │ ├── report.ts │ ├── research.ts │ ├── spectator.ts │ ├── star.ts │ ├── trade.ts │ └── user.ts ├── routes │ ├── admin.ts │ ├── announcements.ts │ ├── auth.ts │ ├── badges.ts │ ├── carrier.ts │ ├── colour.ts │ ├── conversations.ts │ ├── diplomacy.ts │ ├── events.ts │ ├── games.ts │ ├── guilds.ts │ ├── index.ts │ ├── ledger.ts │ ├── report.ts │ ├── research.ts │ ├── shop.ts │ ├── specialist.ts │ ├── spectator.ts │ ├── star.ts │ ├── trade.ts │ └── user.ts ├── singleRoute.ts ├── typedapi │ └── routes.ts └── validate.ts ├── config ├── game │ ├── aliases.json │ ├── avatars.json │ ├── badges.json │ ├── colours.json │ ├── flux.json │ ├── gameNames.json │ ├── levels.json │ ├── settings │ │ ├── official │ │ │ ├── 16player │ │ │ │ ├── 16player_realTime.json │ │ │ │ └── 16player_turnBased.json │ │ │ ├── 1v1.json │ │ │ ├── 1v1turnBased.json │ │ │ ├── 32player_normal.json │ │ │ ├── 32player_ultradark.json │ │ │ ├── newPlayer.json │ │ │ ├── special_anonymous.json │ │ │ ├── special_arcade.json │ │ │ ├── special_battleRoyale.json │ │ │ ├── special_dark.json │ │ │ ├── special_fog.json │ │ │ ├── special_freeForAll.json │ │ │ ├── special_homeStar.json │ │ │ ├── special_homeStarElimination.json │ │ │ ├── special_kingOfTheHill.json │ │ │ ├── special_orbital.json │ │ │ ├── special_tinyGalaxy.json │ │ │ ├── special_ultraDark.json │ │ │ ├── standard.json │ │ │ └── turnBased.json │ │ ├── options.json │ │ └── user │ │ │ ├── customGalaxyExample.json │ │ │ ├── standard.json │ │ │ ├── tutorial.json │ │ │ ├── tutorial_combat_basics.json │ │ │ ├── tutorial_fleet_movement.json │ │ │ ├── tutorial_infrastructure_and_expansion.json │ │ │ ├── tutorial_science_and_research.json │ │ │ ├── tutorial_special_star_types.json │ │ │ └── tutorial_stars_and_carriers.json │ ├── specialStars.json │ ├── specialists.json │ ├── starNames.json │ └── tutorials.json ├── index.ts ├── officialGames.ts └── types │ └── Config.ts ├── db ├── index.ts ├── models │ ├── Announcement.ts │ ├── Event.ts │ ├── Game.ts │ ├── Guild.ts │ ├── History.ts │ ├── Payment.ts │ ├── Report.ts │ ├── User.ts │ └── schemas │ │ ├── action.ts │ │ ├── announcement.ts │ │ ├── badge.ts │ │ ├── carrier.ts │ │ ├── conversation.ts │ │ ├── conversationMessage.ts │ │ ├── event.ts │ │ ├── game.ts │ │ ├── guild.ts │ │ ├── history.ts │ │ ├── payment.ts │ │ ├── player.ts │ │ ├── report.ts │ │ ├── star.ts │ │ ├── team.ts │ │ ├── user.ts │ │ └── waypoint.ts └── scripts │ ├── banSpecsInGame.txt │ ├── clearGameQuitters.txt │ ├── deleteMessage.txt │ ├── fixPlayerCredits.txt │ ├── globalResearchStats.txt │ ├── openPlayerSlot.txt │ ├── pauseInProgressGames.txt │ ├── resetAchievements.txt │ ├── sanitiseDatabase.txt │ ├── sanitiseDatabaseNoUsers.txt │ ├── specialistHireStats.txt │ ├── undefeatPlayer.txt │ └── whoIsPlayer.txt ├── errors └── validation.ts ├── jobs ├── cleanupGamesTimedOut.ts ├── cleanupOldGameHistory.ts ├── cleanupOldTutorials.ts ├── gameTick.ts ├── index.ts ├── officialGamesCheck.ts └── scheduler │ ├── job.ts │ ├── persistence.ts │ └── scheduler.ts ├── package.json ├── services ├── achievement.ts ├── admin.ts ├── ai.ts ├── announcement.ts ├── auth.ts ├── avatar.ts ├── badge.ts ├── basicAi.ts ├── battleRoyale.ts ├── broadcast.ts ├── cache.ts ├── carrier.ts ├── carrierGift.ts ├── carrierMovement.ts ├── combat.ts ├── conversation.ts ├── diplomacy.ts ├── diplomacyUpkeep.ts ├── discord.ts ├── distance.ts ├── email.ts ├── emailTemplates │ ├── forgotUsername.html │ ├── gameCycleSummary.html │ ├── gameFinished.html │ ├── gamePlayerAfk.html │ ├── gameTimedOut.html │ ├── gameWelcome.html │ ├── nextTurnReminder.html │ ├── resetPassword.html │ ├── reviewReminder.html │ ├── welcomeEmail.html │ └── yourTurnReminder.html ├── event.ts ├── game.ts ├── gameAuth.ts ├── gameCreate.ts ├── gameCreateValidation.ts ├── gameFlux.ts ├── gameGalaxy.ts ├── gameJoin.ts ├── gameList.ts ├── gameLock.ts ├── gameMaskingService.ts ├── gameMutex.ts ├── gamePlayerMutex.ts ├── gameState.ts ├── gameTick.ts ├── gameType.ts ├── guild.ts ├── guildUser.ts ├── history.ts ├── index.ts ├── leaderboard.ts ├── ledger.ts ├── map.ts ├── maps │ ├── circular.ts │ ├── circularBalanced.ts │ ├── custom.ts │ ├── doughnut.ts │ ├── irregular.ts │ └── spiral.ts ├── mutex.ts ├── name.ts ├── notification.ts ├── password.ts ├── pathfinding.ts ├── paypal.ts ├── player.ts ├── playerAfk.ts ├── playerColour.ts ├── playerCredits.ts ├── playerCycleRewards.ts ├── playerReady.ts ├── playerStatistics.ts ├── random.ts ├── rating.ts ├── report.ts ├── repository.ts ├── reputation.ts ├── research.ts ├── resource.ts ├── scheduleBuy.ts ├── session.ts ├── ship.ts ├── shipTransfer.ts ├── socket.ts ├── specialStarBan.ts ├── specialist.ts ├── specialistBan.ts ├── specialistHire.ts ├── spectator.ts ├── star.ts ├── starCapture.ts ├── starContested.ts ├── starDistance.ts ├── starMovement.ts ├── starUpgrade.ts ├── team.ts ├── technology.ts ├── trade.ts ├── tutorial.ts ├── types │ ├── Ai.ts │ ├── Announcement.ts │ ├── Avatar.ts │ ├── Badge.ts │ ├── Carrier.ts │ ├── CarrierWaypoint.ts │ ├── Combat.ts │ ├── Conversation.ts │ ├── ConversationMessage.ts │ ├── DBObjectId.ts │ ├── DependencyContainer.ts │ ├── Dimensions.ts │ ├── Diplomacy.ts │ ├── Email.ts │ ├── Flux.ts │ ├── Game.ts │ ├── GameEvent.ts │ ├── GameHistory.ts │ ├── GameMutexLock.ts │ ├── GameTick.ts │ ├── Guild.ts │ ├── InfrastructureUpgrade.ts │ ├── Leaderboard.ts │ ├── Location.ts │ ├── Map.ts │ ├── Mutex.ts │ ├── MutexLock.ts │ ├── Payment.ts │ ├── Player.ts │ ├── PlayerMutexLock.ts │ ├── Rating.ts │ ├── Report.ts │ ├── Request.ts │ ├── SessionData.ts │ ├── SpecialStar.ts │ ├── Specialist.ts │ ├── Star.ts │ ├── Trade.ts │ ├── Tutorial.ts │ ├── User.ts │ ├── UserLevel.ts │ └── events │ │ ├── BaseGameEvent.ts │ │ ├── ConversationMessageSent.ts │ │ ├── GameDiplomacyPeaceDeclared.ts │ │ ├── GameDiplomacyWarDeclared.ts │ │ ├── GameEnded.ts │ │ ├── GamePlayerAFK.ts │ │ ├── GamePlayerBadgePurchased.ts │ │ ├── GamePlayerDefeated.ts │ │ ├── GamePlayerJoined.ts │ │ ├── GamePlayerQuit.ts │ │ ├── GameTurnEnded.ts │ │ └── PlayerGalacticCycleComplete.ts ├── user.ts ├── userLeaderboard.ts ├── userLevel.ts ├── utils.ts └── waypoint.ts ├── sockets ├── serverStub.ts ├── socketEmitters │ ├── diplomacy.ts │ ├── game.ts │ ├── player.ts │ ├── serverSocketEmitter.ts │ └── user.ts ├── socketEventNames │ └── server.ts └── socketHandlers │ ├── player.ts │ ├── serverHandler.ts │ ├── serverSocketHandler.ts │ └── user.ts ├── spec ├── achievement.spec.ts ├── auth.spec.ts ├── carrier.spec.ts ├── combat.spec.ts ├── damage.spec.ts ├── diplomacy.spec.ts ├── gameList.spec.ts ├── leaderboard.spec.ts ├── map.spec.ts ├── player.spec.ts ├── playerAfk.spec.ts ├── random.spec.ts ├── reputation.spec.ts ├── specialistHireCarrier.spec.ts ├── specialistHireStar.spec.ts ├── star.spec.ts ├── starName.spec.ts ├── support │ └── jasmine.json ├── technology.spec.ts ├── user.spec.ts ├── validation │ └── validate.spec.ts └── warpSpeed.spec.ts ├── tools ├── migrate.ts ├── migrations │ ├── migrateBadges.ts │ └── migrateCombatResolution.ts ├── recalculateRankings.ts ├── restoreGame.ts └── tool.ts ├── tsconfig.json └── utils ├── logging.ts └── randomGen.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{js,ts,vue,jsx,tsx,json}] 4 | indent_style = space 5 | indent_size = 4 -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: limitingfactor 14 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 15 | -------------------------------------------------------------------------------- /.github/workflows/nodejs-ci.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | env: 12 | directory-client: ./client 13 | directory-server: ./server 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [20.x] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v1 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | 28 | - run: npm ci 29 | - run: npm run check 30 | - run: npm run build --if-present 31 | - run: npm test 32 | working-directory: ${{ env.directory-server }} 33 | env: 34 | CI: true 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist 4 | build 5 | 6 | # global env files 7 | .env 8 | 9 | # local env files 10 | .env.local 11 | .env.*.local 12 | 13 | # Log files 14 | npm-debug.log* 15 | yarn-debug.log* 16 | yarn-error.log* 17 | 18 | # Editor directories and files 19 | .idea 20 | .vscode/** 21 | .vs/** 22 | !.vscode/launch.json 23 | *.suo 24 | *.ntvs* 25 | *.njsproj 26 | *.sln 27 | *.sw? 28 | 29 | # Mongo playground files 30 | *.mongodb 31 | 32 | # local db folder when using docker-compose 33 | database_data -------------------------------------------------------------------------------- /client/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /client/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /client/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /client/.env.example: -------------------------------------------------------------------------------- 1 | VUE_APP_API_HOST=http://localhost:3000 2 | VUE_APP_SOCKETS_HOST=localhost:3000 3 | VUE_APP_DOCUMENTATION_URL=https://solaris-games.github.io/solaris-docs 4 | VUE_APP_DISCORD_OAUTH_URL=https://discord.com/api/oauth2/authorize?... 5 | -------------------------------------------------------------------------------- /client/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package*.json ./ 6 | RUN npm install 7 | 8 | COPY . . 9 | 10 | CMD ["npm", "run", "dev"] 11 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # client 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run dev 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Locally runs the production bundle (needs to be compiled first) 19 | ``` 20 | npm run serve 21 | ``` 22 | 23 | ### Run your tests 24 | ``` 25 | npm run test 26 | ``` 27 | 28 | ### Lints and fixes files 29 | ``` 30 | npm run lint 31 | ``` 32 | 33 | ### Customize configuration 34 | See [Configuration Reference](https://cli.vuejs.org/config/). 35 | -------------------------------------------------------------------------------- /client/public/assets/LICENSE: -------------------------------------------------------------------------------- 1 | LICENSE CERTIFICATE : Envato Market Item 2 | ============================================== 3 | 4 | This document certifies the purchase of: 5 | ONE REGULAR LICENSE 6 | as defined in the standard terms and conditions on Envato Market. 7 | 8 | Licensor's Author Username: 9 | SeanTheme 10 | 11 | Licensee: 12 | Mike Eason 13 | 14 | Item Title: 15 | HUD - Bootstrap 5 Admin Template 16 | 17 | Item URL: 18 | https://themeforest.net/item/hud-bootstrap-5-admin-template/34000752 19 | 20 | Item ID: 21 | 34000752 22 | 23 | Item Purchase Code: 24 | ddbcee94-ba35-43df-ac07-30977c65df6a 25 | 26 | Purchase Date: 27 | 2022-06-07 07:55:39 UTC 28 | 29 | For any queries related to this document or license please contact Help Team via https://help.market.envato.com 30 | 31 | Envato Pty. Ltd. (ABN 11 119 159 741) 32 | PO Box 16122, Collins Street West, VIC 8007, Australia 33 | 34 | ==== THIS IS NOT A TAX RECEIPT OR INVOICE ==== -------------------------------------------------------------------------------- /client/public/assets/css/fonts/bootstrap-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/public/assets/css/fonts/bootstrap-icons.woff -------------------------------------------------------------------------------- /client/public/assets/css/fonts/bootstrap-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/public/assets/css/fonts/bootstrap-icons.woff2 -------------------------------------------------------------------------------- /client/public/assets/css/images/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/public/assets/css/images/cover.jpg -------------------------------------------------------------------------------- /client/public/assets/css/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/public/assets/css/images/logo.png -------------------------------------------------------------------------------- /client/public/assets/css/images/pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/public/assets/css/images/pattern.png -------------------------------------------------------------------------------- /client/public/assets/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/public/assets/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /client/public/assets/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/public/assets/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /client/public/assets/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/public/assets/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /client/public/assets/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/public/assets/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /client/public/assets/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/public/assets/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /client/public/assets/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/public/assets/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /client/public/assets/webfonts/fa-v4compatibility.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/public/assets/webfonts/fa-v4compatibility.ttf -------------------------------------------------------------------------------- /client/public/assets/webfonts/fa-v4compatibility.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/public/assets/webfonts/fa-v4compatibility.woff2 -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/public/favicon.ico -------------------------------------------------------------------------------- /client/public/favicon_dark.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/public/favicon_dark.ico -------------------------------------------------------------------------------- /client/public/favicon_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/public/favicon_touch.png -------------------------------------------------------------------------------- /client/public/solaris_icon-android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/public/solaris_icon-android.png -------------------------------------------------------------------------------- /client/public/solaris_icon-ipad-r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/public/solaris_icon-ipad-r.png -------------------------------------------------------------------------------- /client/public/solaris_icon-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/public/solaris_icon-ipad.png -------------------------------------------------------------------------------- /client/public/solaris_icon-iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/public/solaris_icon-iphone.png -------------------------------------------------------------------------------- /client/src/assets/audio/backspace.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/audio/backspace.mp3 -------------------------------------------------------------------------------- /client/src/assets/audio/click.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/audio/click.mp3 -------------------------------------------------------------------------------- /client/src/assets/audio/close.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/audio/close.mp3 -------------------------------------------------------------------------------- /client/src/assets/audio/dialog_open.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/audio/dialog_open.mp3 -------------------------------------------------------------------------------- /client/src/assets/audio/download.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/audio/download.mp3 -------------------------------------------------------------------------------- /client/src/assets/audio/hover.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/audio/hover.mp3 -------------------------------------------------------------------------------- /client/src/assets/audio/join.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/audio/join.mp3 -------------------------------------------------------------------------------- /client/src/assets/audio/leave.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/audio/leave.mp3 -------------------------------------------------------------------------------- /client/src/assets/audio/loading.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/audio/loading.mp3 -------------------------------------------------------------------------------- /client/src/assets/audio/open.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/audio/open.mp3 -------------------------------------------------------------------------------- /client/src/assets/audio/quit.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/audio/quit.mp3 -------------------------------------------------------------------------------- /client/src/assets/audio/type.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/audio/type.mp3 -------------------------------------------------------------------------------- /client/src/assets/avatars/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/0.png -------------------------------------------------------------------------------- /client/src/assets/avatars/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/1.png -------------------------------------------------------------------------------- /client/src/assets/avatars/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/10.png -------------------------------------------------------------------------------- /client/src/assets/avatars/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/11.png -------------------------------------------------------------------------------- /client/src/assets/avatars/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/12.png -------------------------------------------------------------------------------- /client/src/assets/avatars/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/13.png -------------------------------------------------------------------------------- /client/src/assets/avatars/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/14.png -------------------------------------------------------------------------------- /client/src/assets/avatars/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/15.png -------------------------------------------------------------------------------- /client/src/assets/avatars/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/16.png -------------------------------------------------------------------------------- /client/src/assets/avatars/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/17.png -------------------------------------------------------------------------------- /client/src/assets/avatars/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/18.png -------------------------------------------------------------------------------- /client/src/assets/avatars/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/19.png -------------------------------------------------------------------------------- /client/src/assets/avatars/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/2.png -------------------------------------------------------------------------------- /client/src/assets/avatars/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/20.png -------------------------------------------------------------------------------- /client/src/assets/avatars/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/21.png -------------------------------------------------------------------------------- /client/src/assets/avatars/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/22.png -------------------------------------------------------------------------------- /client/src/assets/avatars/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/23.png -------------------------------------------------------------------------------- /client/src/assets/avatars/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/24.png -------------------------------------------------------------------------------- /client/src/assets/avatars/25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/25.png -------------------------------------------------------------------------------- /client/src/assets/avatars/26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/26.png -------------------------------------------------------------------------------- /client/src/assets/avatars/27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/27.png -------------------------------------------------------------------------------- /client/src/assets/avatars/28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/28.png -------------------------------------------------------------------------------- /client/src/assets/avatars/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/29.png -------------------------------------------------------------------------------- /client/src/assets/avatars/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/3.png -------------------------------------------------------------------------------- /client/src/assets/avatars/30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/30.png -------------------------------------------------------------------------------- /client/src/assets/avatars/31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/31.png -------------------------------------------------------------------------------- /client/src/assets/avatars/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/32.png -------------------------------------------------------------------------------- /client/src/assets/avatars/33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/33.png -------------------------------------------------------------------------------- /client/src/assets/avatars/34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/34.png -------------------------------------------------------------------------------- /client/src/assets/avatars/35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/35.png -------------------------------------------------------------------------------- /client/src/assets/avatars/36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/36.png -------------------------------------------------------------------------------- /client/src/assets/avatars/37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/37.png -------------------------------------------------------------------------------- /client/src/assets/avatars/38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/38.png -------------------------------------------------------------------------------- /client/src/assets/avatars/39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/39.png -------------------------------------------------------------------------------- /client/src/assets/avatars/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/4.png -------------------------------------------------------------------------------- /client/src/assets/avatars/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/40.png -------------------------------------------------------------------------------- /client/src/assets/avatars/41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/41.png -------------------------------------------------------------------------------- /client/src/assets/avatars/42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/42.png -------------------------------------------------------------------------------- /client/src/assets/avatars/43.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/43.png -------------------------------------------------------------------------------- /client/src/assets/avatars/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/5.png -------------------------------------------------------------------------------- /client/src/assets/avatars/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/6.png -------------------------------------------------------------------------------- /client/src/assets/avatars/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/7.png -------------------------------------------------------------------------------- /client/src/assets/avatars/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/8.png -------------------------------------------------------------------------------- /client/src/assets/avatars/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/9.png -------------------------------------------------------------------------------- /client/src/assets/avatars/ape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/ape.png -------------------------------------------------------------------------------- /client/src/assets/avatars/fab.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/fab.gif -------------------------------------------------------------------------------- /client/src/assets/avatars/red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/avatars/red.png -------------------------------------------------------------------------------- /client/src/assets/badges/LICENSE: -------------------------------------------------------------------------------- 1 | LICENSE CERTIFICATE : Envato Market Item 2 | ============================================== 3 | 4 | This document certifies the purchase of: 5 | ONE REGULAR LICENSE 6 | as defined in the standard terms and conditions on Envato Market. 7 | 8 | Licensor's Author Username: 9 | a-ravlik 10 | 11 | Licensee: 12 | Mike Eason 13 | 14 | Item Title: 15 | Sci-fi Game Achievement 16 | 17 | Item URL: 18 | https://graphicriver.net/item/scifi-game-achievement/26209287 19 | 20 | Item ID: 21 | 26209287 22 | 23 | Item Purchase Code: 24 | 83f39169-6e50-4d87-91d5-0d7bd7a627d1 25 | 26 | Purchase Date: 27 | 2021-12-21 20:23:36 UTC 28 | 29 | For any queries related to this document or license please contact Help Team via https://help.market.envato.com 30 | 31 | Envato Pty. Ltd. (ABN 11 119 159 741) 32 | PO Box 16122, Collins Street West, VIC 8007, Australia 33 | 34 | ==== THIS IS NOT A TAX RECEIPT OR INVOICE ==== 35 | 36 | -------------------------------------------------------------------------------- /client/src/assets/badges/ally.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/badges/ally.png -------------------------------------------------------------------------------- /client/src/assets/badges/dauntless.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/badges/dauntless.png -------------------------------------------------------------------------------- /client/src/assets/badges/diplomat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/badges/diplomat.png -------------------------------------------------------------------------------- /client/src/assets/badges/enemy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/badges/enemy.png -------------------------------------------------------------------------------- /client/src/assets/badges/roleplay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/badges/roleplay.png -------------------------------------------------------------------------------- /client/src/assets/badges/sleepless.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/badges/sleepless.png -------------------------------------------------------------------------------- /client/src/assets/badges/special_anonymous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/badges/special_anonymous.png -------------------------------------------------------------------------------- /client/src/assets/badges/special_arcade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/badges/special_arcade.png -------------------------------------------------------------------------------- /client/src/assets/badges/special_battleRoyale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/badges/special_battleRoyale.png -------------------------------------------------------------------------------- /client/src/assets/badges/special_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/badges/special_dark.png -------------------------------------------------------------------------------- /client/src/assets/badges/special_fog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/badges/special_fog.png -------------------------------------------------------------------------------- /client/src/assets/badges/special_freeForAll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/badges/special_freeForAll.png -------------------------------------------------------------------------------- /client/src/assets/badges/special_homeStar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/badges/special_homeStar.png -------------------------------------------------------------------------------- /client/src/assets/badges/special_homeStarElimination.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/badges/special_homeStarElimination.png -------------------------------------------------------------------------------- /client/src/assets/badges/special_kingOfTheHill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/badges/special_kingOfTheHill.png -------------------------------------------------------------------------------- /client/src/assets/badges/special_orbital.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/badges/special_orbital.png -------------------------------------------------------------------------------- /client/src/assets/badges/special_tinyGalaxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/badges/special_tinyGalaxy.png -------------------------------------------------------------------------------- /client/src/assets/badges/special_ultraDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/badges/special_ultraDark.png -------------------------------------------------------------------------------- /client/src/assets/badges/strategist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/badges/strategist.png -------------------------------------------------------------------------------- /client/src/assets/badges/victor32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/badges/victor32.png -------------------------------------------------------------------------------- /client/src/assets/fonts/ChakraPetch-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/fonts/ChakraPetch-Medium.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/ChakraPetch-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/fonts/ChakraPetch-Regular.ttf -------------------------------------------------------------------------------- /client/src/assets/levels/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/levels/1.png -------------------------------------------------------------------------------- /client/src/assets/levels/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/levels/10.png -------------------------------------------------------------------------------- /client/src/assets/levels/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/levels/2.png -------------------------------------------------------------------------------- /client/src/assets/levels/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/levels/3.png -------------------------------------------------------------------------------- /client/src/assets/levels/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/levels/4.png -------------------------------------------------------------------------------- /client/src/assets/levels/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/levels/5.png -------------------------------------------------------------------------------- /client/src/assets/levels/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/levels/6.png -------------------------------------------------------------------------------- /client/src/assets/levels/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/levels/7.png -------------------------------------------------------------------------------- /client/src/assets/levels/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/levels/8.png -------------------------------------------------------------------------------- /client/src/assets/levels/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/levels/9.png -------------------------------------------------------------------------------- /client/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/logo.png -------------------------------------------------------------------------------- /client/src/assets/map-objects-symbols/128x128_star_black_hole.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | -------------------------------------------------------------------------------- /client/src/assets/map-objects-symbols/128x128_star_pulsar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /client/src/assets/map-objects-symbols/neb0-starless-bright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/map-objects-symbols/neb0-starless-bright.png -------------------------------------------------------------------------------- /client/src/assets/map-objects-symbols/vortex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/map-objects-symbols/vortex.png -------------------------------------------------------------------------------- /client/src/assets/map-objects/128x128_star_black_hole.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /client/src/assets/map-objects/128x128_star_black_hole_binary.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /client/src/assets/nebula/neb0-starless-bright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/nebula/neb0-starless-bright.png -------------------------------------------------------------------------------- /client/src/assets/nebula/neb0-starless.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/nebula/neb0-starless.png -------------------------------------------------------------------------------- /client/src/assets/nebula/neb1-starless-bright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/nebula/neb1-starless-bright.png -------------------------------------------------------------------------------- /client/src/assets/nebula/neb1-starless.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/nebula/neb1-starless.png -------------------------------------------------------------------------------- /client/src/assets/nebula/star-nebula-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/nebula/star-nebula-0.png -------------------------------------------------------------------------------- /client/src/assets/nebula/star-nebula-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/nebula/star-nebula-1.png -------------------------------------------------------------------------------- /client/src/assets/nebula/star-nebula-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/nebula/star-nebula-2.png -------------------------------------------------------------------------------- /client/src/assets/parallax/layer1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/parallax/layer1.png -------------------------------------------------------------------------------- /client/src/assets/parallax/layer2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/parallax/layer2.png -------------------------------------------------------------------------------- /client/src/assets/parallax/layer3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/parallax/layer3.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/16_player_relaxed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/16_player_relaxed.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/1v1_rt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/1v1_rt.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/1v1_tb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/1v1_tb.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/32_player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/32_player.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/codex/carrier1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/codex/carrier1.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/codex/carrier2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/codex/carrier2.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/codex/eventlog1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/codex/eventlog1.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/codex/map1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/codex/map1.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/codex/research1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/codex/research1.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/codex/star1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/codex/star1.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/codex/star2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/codex/star2.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/codex/star3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/codex/star3.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/codex/star4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/codex/star4.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/codex/star5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/codex/star5.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/featured.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/featured.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/game-carousel-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/game-carousel-1.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/game-carousel-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/game-carousel-2.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/game-carousel-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/game-carousel-3.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/game1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/game1.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/home-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/home-1.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/home-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/home-2.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/home-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/home-3.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/home-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/home-4.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/home-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/home-5.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/new_player_rt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/new_player_rt.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/special_anonymous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/special_anonymous.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/special_arcade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/special_arcade.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/special_battleRoyale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/special_battleRoyale.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/special_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/special_dark.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/special_fog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/special_fog.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/special_freeForAll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/special_freeForAll.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/special_homeStar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/special_homeStar.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/special_homeStarElimination.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/special_homeStarElimination.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/special_kingOfTheHill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/special_kingOfTheHill.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/special_orbital.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/special_orbital.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/special_tinyGalaxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/special_tinyGalaxy.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/special_ultraDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/special_ultraDark.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/standard_rt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/standard_rt.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/standard_tb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/standard_tb.png -------------------------------------------------------------------------------- /client/src/assets/screenshots/tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/screenshots/tutorial.png -------------------------------------------------------------------------------- /client/src/assets/solaris_logo_text_1000w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/solaris_logo_text_1000w.png -------------------------------------------------------------------------------- /client/src/assets/specialists/afterburn.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/src/assets/specialists/airtight-hatch.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/src/assets/specialists/bolter-gun.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/src/assets/specialists/cyborg-face.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/src/assets/specialists/eclipse.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/src/assets/specialists/energy-tank.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/src/assets/specialists/forward-field.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/src/assets/specialists/metal-scales.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/src/assets/specialists/power-generator.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/src/assets/specialists/rocket-flight.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/src/assets/specialists/sentry-gun.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/src/assets/specialists/spoutnik.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/src/assets/specialists/walking-scout.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/src/assets/stars/128x128_star_pulsar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 11 | 14 | 17 | 20 | -------------------------------------------------------------------------------- /client/src/assets/stars/star-asteroid-field-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/stars/star-asteroid-field-0.png -------------------------------------------------------------------------------- /client/src/assets/stars/star-asteroid-field-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/stars/star-asteroid-field-1.png -------------------------------------------------------------------------------- /client/src/assets/stars/star-asteroid-field-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/stars/star-asteroid-field-2.png -------------------------------------------------------------------------------- /client/src/assets/stars/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/stars/star.png -------------------------------------------------------------------------------- /client/src/assets/stars/vortex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solaris-games/solaris/5a3f6766b84efffe263c0356c6012a41a52afa6c/client/src/assets/stars/vortex.png -------------------------------------------------------------------------------- /client/src/assets/styles.css: -------------------------------------------------------------------------------- 1 | .bg-info { 2 | color: white; 3 | /*font-weight: 400;*/ 4 | } 5 | 6 | .bg-info .small, .bg-info small { 7 | font-size: .925em; 8 | } 9 | 10 | @font-face { 11 | font-family:"Chakra Petch"; /* no space between colon and property or firefox will add warnings for some reason */ 12 | src: url('fonts/ChakraPetch-Regular.ttf'); 13 | font-weight: 300; 14 | } 15 | 16 | @font-face { 17 | font-family:"Chakra Petch"; /* no space between colon and property or firefox will add warnings for some reason */ 18 | src: url('fonts/ChakraPetch-Medium.ttf'); 19 | font-weight: 500; 20 | } -------------------------------------------------------------------------------- /client/src/eventBusEventNames/diplomacy.ts: -------------------------------------------------------------------------------- 1 | import type { DiplomaticStatus } from "@solaris-common"; 2 | import { makeCastFunc } from "@solaris-common"; 3 | import type { EventBusEventName } from "./eventBusEventName"; 4 | 5 | export type DiplomacyEventBusEventType = { diplomacyEventBusEventType: 'diplomacyEventBusEventType' }; 6 | export type DiplomacyEventBusEventName = EventBusEventName & { diplomacyEventBusEventName: 'diplomacyEventBusEventName' } 7 | 8 | const toEventName: (value: string) => DiplomacyEventBusEventName = makeCastFunc(); 9 | 10 | export default class DiplomacyEventBusEventNames { 11 | private constructor() { }; 12 | 13 | public static readonly PlayerDiplomaticStatusChanged: DiplomacyEventBusEventName<{ diplomaticStatus: DiplomaticStatus }> = toEventName('playerDiplomaticStatusChanged'); 14 | } 15 | -------------------------------------------------------------------------------- /client/src/eventBusEventNames/eventBusEventName.ts: -------------------------------------------------------------------------------- 1 | export type EventBusEventName = string & { eventBusEventType?: TEventBusEventType, data?: TData, eventBusEventName: 'eventBusEventName' } 2 | -------------------------------------------------------------------------------- /client/src/eventBusEventNames/game.ts: -------------------------------------------------------------------------------- 1 | import type { GameState } from "@solaris-common"; 2 | import { makeCastFunc } from "@solaris-common"; 3 | import type { EventBusEventName } from "./eventBusEventName"; 4 | 5 | export type GameEventBusEventType = { gameEventBusEventType: 'gameEventBusEventType' }; 6 | export type GameEventBusEventName = EventBusEventName & { gameEventBusEventName: 'gameEventBusEventName' } 7 | 8 | const toEventName: (value: string) => GameEventBusEventName = makeCastFunc(); 9 | 10 | export default class GameEventBusEventNames { 11 | private constructor() { }; 12 | 13 | public static readonly GameStarted: GameEventBusEventName<{ state: GameState }> = toEventName('gameStarted'); 14 | public static readonly OnGameTick: GameEventBusEventName = toEventName('onGameTick'); 15 | } 16 | -------------------------------------------------------------------------------- /client/src/eventBusEventNames/user.ts: -------------------------------------------------------------------------------- 1 | import type {EventBusEventName} from "./eventBusEventName"; 2 | import {type ConversationMessageSentResult, makeCastFunc} from "@solaris-common"; 3 | 4 | export type UserEventBusEventType = { playerEventBusEventType: 'playerEventBusEventType' }; 5 | export type UserEventBusEventName = EventBusEventName & { playerEventBusEventName: 'playerEventBusEventName' } 6 | 7 | const toEventName: (value: string) => UserEventBusEventName = makeCastFunc(); 8 | 9 | export default class UserEventBusEventNames { 10 | public static readonly GameMessageSent: UserEventBusEventName> = toEventName('gameMessageSent'); 11 | } 12 | -------------------------------------------------------------------------------- /client/src/game/eventEmitter.ts: -------------------------------------------------------------------------------- 1 | import mitt, { type Emitter } from 'mitt'; 2 | 3 | // longer term, we want to rework rendering code anyways 4 | export class EventEmitter { 5 | ev: Emitter; 6 | 7 | constructor() { 8 | this.ev = mitt(); 9 | } 10 | 11 | on(event, handler) { 12 | this.ev.on(event, handler); 13 | } 14 | 15 | off(event, handler) { 16 | this.ev.off(event, handler); 17 | } 18 | 19 | emit(event, data) { 20 | this.ev.emit(event, data); 21 | } 22 | 23 | removeAllListeners() { 24 | this.ev.all.clear(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /client/src/game/highlight.ts: -------------------------------------------------------------------------------- 1 | import type { Location } from '@solaris-common'; 2 | import { Graphics } from 'pixi.js'; 3 | 4 | const STAR_HIGHLIGHT_RADIUS = 12; 5 | 6 | export const createStarHighlight = (location: Location, alpha = 1) => { 7 | const graphics = new Graphics(); 8 | const radius = STAR_HIGHLIGHT_RADIUS; 9 | 10 | graphics.star(location.x, location.y, radius, radius, radius - 3); 11 | graphics.stroke({ 12 | width: 1, 13 | color: 0xFFFFFF, 14 | alpha, 15 | }); 16 | 17 | return graphics; 18 | } 19 | -------------------------------------------------------------------------------- /client/src/game/mapObject.ts: -------------------------------------------------------------------------------- 1 | import type { Location } from "@solaris-common"; 2 | import { EventEmitter } from "./eventEmitter"; 3 | import type { Container } from "pixi.js"; 4 | 5 | export abstract class MapObject extends EventEmitter { 6 | abstract getLocation(): Location; 7 | abstract getContainer(): Container; 8 | abstract onZoomChanging(zoomPercent: number): void; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /client/src/game/screenshot.ts: -------------------------------------------------------------------------------- 1 | import type { Game } from "../types/game"; 2 | import { type GameContainer } from "./container"; 3 | 4 | export const screenshot = (container: GameContainer, game: Game, reportGameError: (msg: string) => void) => { 5 | const app = container.app!; 6 | 7 | const filename = `${game.settings.general.name}-${new Date().toISOString()}`; 8 | 9 | 10 | try { 11 | //https://www.pixiplayground.com/#/edit/wffvzGE8E2hwQBWvZGpT4 12 | app.renderer.extract.download({ 13 | filename, 14 | target: container.viewport! 15 | }); 16 | } catch (e) { 17 | console.error("Failed to create image from viewport", e); 18 | 19 | reportGameError("Failed to create image"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/src/integration/messages.ts: -------------------------------------------------------------------------------- 1 | export type MessageData = { 2 | gameID: string, 3 | gameName: string, 4 | fromPlayerId: string | null, 5 | fromPlayerAlias: string, 6 | } 7 | 8 | export interface MessageIntegration { 9 | onConversationMessageReceived: (message: MessageData) => void; 10 | } 11 | -------------------------------------------------------------------------------- /client/src/integration/solaris.ts: -------------------------------------------------------------------------------- 1 | import type {MessageIntegration} from "@/integration/messages"; 2 | 3 | export interface SolarisIntegration { 4 | messages: MessageIntegration; 5 | } 6 | 7 | declare global { 8 | interface Window { 9 | solaris?: SolarisIntegration; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /client/src/mutationNames/gameMutationNames.ts: -------------------------------------------------------------------------------- 1 | export default class GameMutationNames { 2 | private constructor() { }; 3 | 4 | public static readonly GameStarted: string = 'gameStarted'; 5 | } 6 | -------------------------------------------------------------------------------- /client/src/mutationNames/playerMutationNames.ts: -------------------------------------------------------------------------------- 1 | export default class PlayerMutationNames { 2 | private constructor() { }; 3 | 4 | public static readonly GamePlayerJoined: string = 'gamePlayerJoined'; 5 | public static readonly GamePlayerQuit: string = 'gamePlayerQuit'; 6 | 7 | public static readonly GamePlayerReady: string = 'gamePlayerReady'; 8 | public static readonly GamePlayerNotReady: string = 'gamePlayerNotReady'; 9 | 10 | public static readonly GamePlayerReadyToQuit: string = 'gamePlayerReadyToQuit'; 11 | public static readonly GamePlayerNotReadyToQuit: string = 'gamePlayerNotReadyToQuit'; 12 | 13 | public static readonly PlayerDebtSettled: string = 'playerDebtSettled'; 14 | } 15 | -------------------------------------------------------------------------------- /client/src/services/api/announcements.js: -------------------------------------------------------------------------------- 1 | import BaseApiService from "./base"; 2 | import axios from "axios"; 3 | 4 | class AnnouncementsService extends BaseApiService { 5 | getCurrentAnnouncements () { 6 | return axios.get(`${this.BASE_URL}announcements/`, { withCredentials: true }); 7 | } 8 | 9 | getLatestAnnouncement () { 10 | return axios.get(`${this.BASE_URL}announcements/latest/`, { withCredentials: true }); 11 | } 12 | 13 | getAnnouncementState () { 14 | return axios.get(`${this.BASE_URL}announcements/state/`, { withCredentials: true }); 15 | } 16 | 17 | markAsRead () { 18 | return axios.patch(`${this.BASE_URL}announcements/state/markAsRead/`, {}, { withCredentials: true }); 19 | } 20 | } 21 | 22 | export default new AnnouncementsService(); 23 | -------------------------------------------------------------------------------- /client/src/services/api/auth.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import BaseApiService from './base' 3 | 4 | class AuthService extends BaseApiService { 5 | login (email, password) { 6 | return axios.post(this.BASE_URL + 'auth/login', { 7 | email: email, 8 | password: password 9 | }, 10 | { 11 | withCredentials: true 12 | }) 13 | } 14 | 15 | logout () { 16 | return axios.post(this.BASE_URL + 'auth/logout', {}, { withCredentials: true }) 17 | } 18 | 19 | verify () { 20 | return axios.post(this.BASE_URL + 'auth/verify', {}, { withCredentials: true }) 21 | } 22 | 23 | clearOauthDiscord () { 24 | return axios.delete(this.BASE_URL + 'auth/discord', { withCredentials: true }) 25 | } 26 | } 27 | 28 | export default new AuthService() 29 | -------------------------------------------------------------------------------- /client/src/services/api/colour.js: -------------------------------------------------------------------------------- 1 | import BaseApiService from "./base"; 2 | import axios from 'axios' 3 | 4 | class ColourService extends BaseApiService { 5 | listColours () { 6 | return axios.get(this.BASE_URL + 'colour/list', 7 | { withCredentials: true }); 8 | } 9 | 10 | addColour (gameId, data) { 11 | return axios.put(`${this.BASE_URL}game/${gameId}/colour/override`, data, 12 | { withCredentials: true }); 13 | } 14 | } 15 | 16 | export default new ColourService(); 17 | -------------------------------------------------------------------------------- /client/src/services/api/event.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import BaseApiService from './base' 3 | 4 | class EventService extends BaseApiService { 5 | 6 | getEvents (gameId, page, pageSize, category) { 7 | const url = `${this.BASE_URL}game/${gameId}/events?page=${page}&pageSize=${pageSize}&category=${category}` 8 | 9 | return axios.get(url, 10 | { withCredentials: true }) 11 | } 12 | 13 | markEventAsRead (gameId, eventId) { 14 | return axios.patch(this.BASE_URL + 'game/' + gameId + '/events/' + eventId + '/markAsRead', {}, 15 | { withCredentials: true }) 16 | } 17 | 18 | markAllEventsAsRead (gameId) { 19 | return axios.patch(this.BASE_URL + 'game/' + gameId + '/events/markAsRead', {}, 20 | { withCredentials: true }) 21 | } 22 | 23 | getUnreadCount (gameId) { 24 | return axios.get(this.BASE_URL + 'game/' + gameId + '/events/unread', 25 | { withCredentials: true }) 26 | } 27 | 28 | } 29 | 30 | export default new EventService() 31 | -------------------------------------------------------------------------------- /client/src/services/api/report.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import BaseApiService from './base' 3 | 4 | class ReportApiService extends BaseApiService { 5 | 6 | reportPlayer (gameId, playerId, messageId, conversationId, abuse, spamming, multiboxing, inappropriateAlias) { 7 | return axios.post(this.BASE_URL + 'game/' + gameId + '/report', 8 | { 9 | playerId, 10 | messageId, 11 | conversationId, 12 | reasons: { 13 | abuse, 14 | spamming, 15 | multiboxing, 16 | inappropriateAlias 17 | } 18 | }, 19 | { withCredentials: true }) 20 | } 21 | 22 | } 23 | 24 | export default new ReportApiService() 25 | -------------------------------------------------------------------------------- /client/src/services/api/research.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import BaseApiService from './base' 3 | 4 | class ResearchService extends BaseApiService { 5 | updateResearchNow (gameId, preference) { 6 | return axios.put(this.BASE_URL + 'game/' + gameId + '/research/now', { 7 | preference 8 | }, 9 | { withCredentials: true }) 10 | } 11 | 12 | updateResearchNext (gameId, preference) { 13 | return axios.put(this.BASE_URL + 'game/' + gameId + '/research/next', { 14 | preference 15 | }, 16 | { withCredentials: true }) 17 | } 18 | } 19 | 20 | export default new ResearchService() 21 | -------------------------------------------------------------------------------- /client/src/services/api/shop.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import BaseApiService from './base' 3 | 4 | class ShopApiService extends BaseApiService { 5 | 6 | purchaseGalacticCredits (amount) { 7 | return axios.get(this.BASE_URL + 'shop/galacticcredits/purchase?amount=' + amount.toString(), 8 | { withCredentials: true }) 9 | } 10 | 11 | } 12 | 13 | export default new ShopApiService() 14 | -------------------------------------------------------------------------------- /client/src/services/randomHelper.js: -------------------------------------------------------------------------------- 1 | class RandomHelper { 2 | 3 | // Note that the max is INCLUSIVE 4 | getRandomNumberBetween(min, max) { 5 | return Math.floor(Math.random() * (max - min + 1) + min); 6 | } 7 | 8 | } 9 | 10 | export default new RandomHelper() -------------------------------------------------------------------------------- /client/src/services/typedapi/admin.ts: -------------------------------------------------------------------------------- 1 | import {type Announcement, createAdminRoutes, type ListPasswordReset, type ListUser} from "@solaris-common"; 2 | import type { Axios } from "axios"; 3 | import {doGet, type ResponseResult} from "."; 4 | 5 | const routes = createAdminRoutes(); 6 | 7 | export const getUsers = (axios: Axios) => async (): Promise[]>> => { 8 | return doGet(axios)(routes.listUsers, {}, {}); 9 | } 10 | 11 | export const getPasswordResets = (axios: Axios) => async (): Promise[]>> => { 12 | return doGet(axios)(routes.listPasswordResets, {}, {}); 13 | } 14 | 15 | export const getAllAnnouncements = (axios: Axios) => async (): Promise[]>> => { 16 | return doGet(axios)(routes.getAllAnnouncements, {}, {}); 17 | } 18 | -------------------------------------------------------------------------------- /client/src/sockets/socketEmitters/clientSocketEmitter.ts: -------------------------------------------------------------------------------- 1 | import type { Socket } from "socket.io-client"; 2 | import {Emitter, type EventName} from "@solaris-common"; 3 | 4 | export abstract class ClientSocketEmitter extends Emitter { 5 | constructor(private socket: Socket) { 6 | super(); 7 | this.eventType; 8 | } 9 | 10 | protected override emit, TData>(event: TEventName, data: TData): void { 11 | this.socket.emit(event, data); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/sockets/socketEmitters/player.ts: -------------------------------------------------------------------------------- 1 | import type { Socket } from "socket.io-client"; 2 | import { PlayerSocketEventNames, type PlayerSocketEventType } from "@solaris-common"; 3 | import type { InjectionKey } from "vue"; 4 | import { ClientSocketEmitter } from "./clientSocketEmitter"; 5 | 6 | export const playerClientSocketEmitterInjectionKey: InjectionKey = Symbol('PlayerClientSocketEmitter'); 7 | 8 | export class PlayerClientSocketEmitter extends ClientSocketEmitter { 9 | constructor(socket: Socket) { 10 | super(socket); 11 | } 12 | 13 | public emitGameRoomJoined(data: { gameId: string, playerId?: string }) { 14 | this.emit(PlayerSocketEventNames.GameRoomJoined, data); 15 | } 16 | 17 | public emitGameRoomLeft(data: { gameId: string, playerId?: string }) { 18 | this.emit(PlayerSocketEventNames.GameRoomLeft, data); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /client/src/sockets/socketEmitters/user.ts: -------------------------------------------------------------------------------- 1 | import {ClientSocketEmitter} from "@/sockets/socketEmitters/clientSocketEmitter"; 2 | import type {UserSocketEventType} from "@solaris-common"; 3 | import type { InjectionKey } from "vue"; 4 | import {UserSocketEventNames} from "@solaris-common"; 5 | import type { Socket } from "socket.io-client"; 6 | 7 | export const userClientSocketEmitterInjectionKey: InjectionKey = Symbol('UserClientSocketEmitter'); 8 | 9 | export class UserClientSocketEmitter extends ClientSocketEmitter { 10 | constructor(socket: Socket) { 11 | super(socket); 12 | } 13 | 14 | public emitJoined() { 15 | this.emit(UserSocketEventNames.UserJoined, {}); 16 | } 17 | 18 | public emitLeft() { 19 | this.emit(UserSocketEventNames.UserLeft, {}); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/src/sockets/socketEventNames/client.ts: -------------------------------------------------------------------------------- 1 | import { type EventName } from "@solaris-common"; 2 | import { makeCastFunc } from "@solaris-common"; 3 | 4 | export type ClientSocketEventType = { clientSocketEventType: 'clientSocketEventType' }; 5 | export type ClientSocketEventName = EventName & { clientSocketEventName: 'clientSocketEventName' }; 6 | 7 | const toEventName: (value: string) => ClientSocketEventName = makeCastFunc(); 8 | 9 | export default class ClientSocketEventNames { 10 | private constructor() { }; 11 | 12 | public static readonly Connect: ClientSocketEventName = toEventName('connect'); 13 | public static readonly Reconnect: ClientSocketEventName = toEventName('reconnect'); 14 | public static readonly Error: ClientSocketEventName = toEventName('error'); 15 | } 16 | -------------------------------------------------------------------------------- /client/src/sockets/socketHandlers/clientSocketHandler.ts: -------------------------------------------------------------------------------- 1 | import type { Socket } from "socket.io-client"; 2 | import {type EventName, Handler} from "@solaris-common"; 3 | 4 | export abstract class ClientSocketHandler extends Handler { 5 | constructor(private socket: Socket) { 6 | super(); 7 | this.eventType; 8 | } 9 | 10 | protected override on, TData>(event: TEventName, listener: (e: TData) => void) { 11 | this.socket.on(event as string, listener); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/sockets/socketHandlers/diplomacy.ts: -------------------------------------------------------------------------------- 1 | import type { Socket } from "socket.io-client"; 2 | import type { DiplomaticStatus } from "@solaris-common"; 3 | import { DiplomacySocketEventNames, type DiplomacySocketEventType } from '@solaris-common'; 4 | import type { EventBus } from "../../eventBus"; 5 | import DiplomacyEventBusEventNames from "../../eventBusEventNames/diplomacy"; 6 | import { ClientSocketHandler } from "./clientSocketHandler"; 7 | 8 | export class DiplomacyClientSocketHandler extends ClientSocketHandler { 9 | constructor(socket: Socket, 10 | eventBus: EventBus) { 11 | super(socket); 12 | 13 | this.on(DiplomacySocketEventNames.PlayerDiplomaticStatusChanged, (e: { diplomaticStatus: DiplomaticStatus }) => eventBus.emit(DiplomacyEventBusEventNames.PlayerDiplomaticStatusChanged, e)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/src/sockets/socketHandlers/user.ts: -------------------------------------------------------------------------------- 1 | import {ClientSocketHandler} from "./clientSocketHandler"; 2 | import type {ConversationMessageSentResult, UserSocketEventType} from "@solaris-common"; 3 | import type {State} from "../../store"; 4 | import type {EventBus} from "../../eventBus"; 5 | import type { Store } from "vuex/types/index.js"; 6 | import type { Socket } from "socket.io-client"; 7 | import {UserSocketEventNames} from "@solaris-common"; 8 | import UserEventBusEventNames from "../../eventBusEventNames/user"; 9 | 10 | export class UserClientSocketHandler extends ClientSocketHandler { 11 | constructor(socket: Socket, 12 | store: Store, 13 | eventBus: EventBus) { 14 | super(socket); 15 | 16 | this.on(UserSocketEventNames.GameMessageSent, (e: ConversationMessageSentResult) => { 17 | eventBus.emit(UserEventBusEventNames.GameMessageSent, e) 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /client/src/types/game.ts: -------------------------------------------------------------------------------- 1 | import type { Game as CGame } from "@solaris-common"; 2 | import type { Player as CPlayer } from "@solaris-common"; 3 | import type { Star as CStar } from "@solaris-common"; 4 | import type { Carrier as CCarrier } from "@solaris-common"; 5 | 6 | export type Game = CGame; 7 | 8 | export type Player = CPlayer; 9 | 10 | export type Star = CStar; 11 | 12 | export type Carrier = CCarrier; 13 | -------------------------------------------------------------------------------- /client/src/types/ruler.ts: -------------------------------------------------------------------------------- 1 | import type {MapObject, Location} from "@solaris-common"; 2 | 3 | export type RulerPoint = { 4 | type: 'star' | 'carrier', 5 | object: MapObject, 6 | location: Location, 7 | distance: number, 8 | }; 9 | -------------------------------------------------------------------------------- /client/src/types/waypoint.ts: -------------------------------------------------------------------------------- 1 | export type TempWaypoint = { 2 | destination: string; 3 | action: string; 4 | actionShips: number; 5 | delayTicks: number; 6 | source: string | undefined | null; 7 | } 8 | -------------------------------------------------------------------------------- /client/src/types/window.ts: -------------------------------------------------------------------------------- 1 | export { }; 2 | 3 | declare global { 4 | interface Window { 5 | bootstrap: unknown, 6 | $: {}, 7 | _solaris: { errors: string[] } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /client/src/util/eventDeduplication.js: -------------------------------------------------------------------------------- 1 | export const attachEventDeduplication = (topLevel, targetView) => { 2 | let lastEvent = null; 3 | 4 | topLevel.addEventListener('pointerup', (e) => { 5 | if (e.target === targetView) { 6 | lastEvent = { 7 | clientX: e.clientX, 8 | clientY: e.clientY, 9 | timeStamp: e.timeStamp 10 | }; 11 | } 12 | }, true); 13 | 14 | topLevel.addEventListener('click', (e) => { 15 | if (lastEvent && e.timeStamp <= lastEvent.timeStamp + 100) { 16 | e.stopPropagation(); 17 | } 18 | 19 | lastEvent = null; 20 | }, true); 21 | }; 22 | -------------------------------------------------------------------------------- /client/src/util/format.ts: -------------------------------------------------------------------------------- 1 | import { type Location } from '@solaris-common'; 2 | 3 | const LIGHTYEARS = 50; 4 | 5 | export const formatLocation = (location: Location) => { 6 | return `(${(location.x/LIGHTYEARS).toFixed(2)}, ${(location.y/LIGHTYEARS).toFixed(2)})`; 7 | } 8 | -------------------------------------------------------------------------------- /client/src/util/http.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | const isISODate = (value: unknown) => { 4 | return typeof value === 'string' && value.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(.\d+)?Z$/); 5 | }; 6 | 7 | export const convertDates = (obj: unknown) => { 8 | if (Array.isArray(obj)) { 9 | return obj.map(convertDates); 10 | } else if (obj !== null && typeof obj === 'object') { 11 | return Object.fromEntries( 12 | Object.entries(obj).map(([key, value]) => [key, convertDates(value)]) 13 | ); 14 | } else if (isISODate(obj)) { 15 | return new Date(obj as string); 16 | } 17 | return obj; 18 | }; 19 | 20 | export const createHttpClient = () => { 21 | const client = axios.create(); 22 | 23 | client.interceptors.response.use((response) => { 24 | if (response.data) { 25 | response.data = convertDates(response.data); 26 | } 27 | return response; 28 | }, (error) => { 29 | return Promise.reject(error); 30 | }); 31 | 32 | return client 33 | } 34 | -------------------------------------------------------------------------------- /client/src/util/keys.ts: -------------------------------------------------------------------------------- 1 | import type { InjectionKey } from "vue"; 2 | import { type ToastPluginApi } from "vue-toast-notification" 3 | 4 | export const toastInjectionKey: InjectionKey = Symbol('toast'); 5 | -------------------------------------------------------------------------------- /client/src/util/markdown.js: -------------------------------------------------------------------------------- 1 | import { marked } from 'marked'; 2 | 3 | marked.use({ 4 | gfm: true, 5 | }); 6 | 7 | export const renderMarkdown = (markdown) => { 8 | return marked.parse(markdown); 9 | }; 10 | -------------------------------------------------------------------------------- /client/src/util/reactiveHooks.ts: -------------------------------------------------------------------------------- 1 | import { type Store } from 'vuex'; 2 | import type {State} from "@/store"; 3 | import { computed } from 'vue'; 4 | 5 | export const useIsHistoricalMode = (store: Store) => { 6 | return computed(() => store.state.tick !== store.state.game.state.tick); 7 | } 8 | -------------------------------------------------------------------------------- /client/src/views/account/Warnings.vue: -------------------------------------------------------------------------------- 1 | 8 | 22 | 27 | -------------------------------------------------------------------------------- /client/src/views/components/FormErrorList.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 15 | 16 | 19 | -------------------------------------------------------------------------------- /client/src/views/components/HelpTooltip.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 15 | 17 | -------------------------------------------------------------------------------- /client/src/views/components/LoadingSpinner.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 17 | 18 | 20 | -------------------------------------------------------------------------------- /client/src/views/components/Logo.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | 15 | 26 | 27 | 36 | -------------------------------------------------------------------------------- /client/src/views/components/Poll.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 19 | 20 | 27 | -------------------------------------------------------------------------------- /client/src/views/components/ViewSubtitle.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 20 | -------------------------------------------------------------------------------- /client/src/views/components/modal/ModalButton.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 19 | -------------------------------------------------------------------------------- /client/src/views/game/components/MenuTitle.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 28 | 29 | 34 | -------------------------------------------------------------------------------- /client/src/views/game/components/diplomacy/DiplomacyIcons.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 28 | 29 | 31 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/CombatActorDescription.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 25 | 31 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/GameDiplomacyPeaceDeclared.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | 22 | 24 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/GameDiplomacyWarDeclared.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | 22 | 24 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/GamePaused.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 22 | 23 | 25 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/GamePlayerAFK.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | 22 | 24 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/GamePlayerBadgePurchased.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | 22 | 24 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/GamePlayerDefeated.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 24 | 25 | 27 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/GamePlayerJoined.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | 22 | 24 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/GamePlayerQuit.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | 22 | 24 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/GameStarted.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 22 | 23 | 25 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/PlayerBulkInfrastructureUpgraded.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 24 | 25 | 27 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/PlayerCarrierSpecialistHired.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 24 | 25 | 27 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/PlayerConversationCreated.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 19 | 20 | 22 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/PlayerConversationInvited.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 19 | 20 | 22 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/PlayerConversationLeft.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 19 | 20 | 22 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/PlayerCreditsReceived.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 34 | 35 | 37 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/PlayerCreditsSent.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 34 | 35 | 37 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/PlayerCreditsSpecialistsReceived.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 34 | 35 | 37 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/PlayerCreditsSpecialistsSent.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 34 | 35 | 37 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/PlayerRenownReceived.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 34 | 35 | 37 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/PlayerRenownSent.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 34 | 35 | 37 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/PlayerResearchComplete.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 29 | 30 | 32 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/PlayerStarAbandoned.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | 22 | 24 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/PlayerStarDied.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | 22 | 24 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/PlayerStarReignited.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | 22 | 24 | -------------------------------------------------------------------------------- /client/src/views/game/components/eventLog/events/PlayerStarSpecialistHired.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 24 | 25 | 27 | -------------------------------------------------------------------------------- /client/src/views/game/components/intel/LineChart.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 35 | 36 | 39 | -------------------------------------------------------------------------------- /client/src/views/game/components/intel/PieChart.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 31 | 32 | 35 | -------------------------------------------------------------------------------- /client/src/views/game/components/intel/PolarAreaChart.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 22 | 23 | 26 | -------------------------------------------------------------------------------- /client/src/views/game/components/menu/CommunityGuidelinesBar.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | 16 | 28 | -------------------------------------------------------------------------------- /client/src/views/game/components/menu/ErrorLog.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 17 | 18 | 21 | -------------------------------------------------------------------------------- /client/src/views/game/components/menu/NotLoggedInBar.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 20 | 21 | 23 | -------------------------------------------------------------------------------- /client/src/views/game/components/player/EloRating.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | 13 | 15 | -------------------------------------------------------------------------------- /client/src/views/game/components/player/PlayerMissedTurns.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | 19 | 21 | -------------------------------------------------------------------------------- /client/src/views/game/components/player/PlayerOpenSlotStatus.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | 19 | 21 | -------------------------------------------------------------------------------- /client/src/views/game/components/player/PlayerReport.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 25 | 26 | 28 | -------------------------------------------------------------------------------- /client/src/views/game/components/settings/GameSettingValue.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 24 | 25 | 28 | -------------------------------------------------------------------------------- /client/src/views/game/components/settings/Settings.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 30 | 31 | 33 | -------------------------------------------------------------------------------- /client/src/views/game/components/shared/OrbitalMechanicsETAWarning.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | 17 | 22 | -------------------------------------------------------------------------------- /client/src/views/game/components/shared/TeamName.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 21 | 22 | 29 | -------------------------------------------------------------------------------- /client/src/views/game/components/welcome/EnterPassword.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 35 | 36 | 38 | -------------------------------------------------------------------------------- /client/src/views/game/components/welcome/NewPlayerMessage.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 21 | 22 | 24 | -------------------------------------------------------------------------------- /client/src/views/shop/PurchaseComplete.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 27 | 28 | 30 | -------------------------------------------------------------------------------- /client/src/views/shop/PurchaseFailed.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 24 | 25 | 27 | -------------------------------------------------------------------------------- /client/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | declare module '*.mp3'; 3 | -------------------------------------------------------------------------------- /client/src/voronoi/Javascript-Voronoi/excanvas/README: -------------------------------------------------------------------------------- 1 | ExplorerCanvas 2 | Copyright 2006 Google Inc. 3 | 4 | ------------------------------------------------------------------------------- 5 | DESCRIPTION 6 | 7 | Firefox, Safari and Opera 9 support the canvas tag to allow 2D command-based 8 | drawing operations. ExplorerCanvas brings the same functionality to Internet 9 | Explorer; web developers only need to include a single script tag in their 10 | existing canvas webpages to enable this support. 11 | 12 | 13 | ------------------------------------------------------------------------------- 14 | INSTALLATION 15 | 16 | Include the ExplorerCanvas tag in the same directory as your HTML files, and 17 | add the following code to your page, preferably in the tag. 18 | 19 | 20 | 21 | If you run into trouble, please look at the included example code to see how 22 | to best implement this -------------------------------------------------------------------------------- /client/src/voronoi/Javascript-Voronoi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "voronoi", 3 | "version": "1.0.0", 4 | "description": "A Javascript implementation of Steven J. Fortune's algorithm to efficiently compute Voronoi diagrams. The Voronoi object's purpose is to solely compute a Voronoi diagram, it is completely standalone, with no dependency on external code: it contains no rendering code: that is left to the user of the library.", 5 | "main": "rhill-voronoi-core.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/gorhill/Javascript-Voronoi.git" 12 | }, 13 | "author": "Raymond Hill", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/gorhill/Javascript-Voronoi/issues" 17 | }, 18 | "homepage": "https://github.com/gorhill/Javascript-Voronoi" 19 | } 20 | -------------------------------------------------------------------------------- /client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noImplicitAny": false, 4 | "paths": { 5 | "@solaris-common": [ "../common/src/index.ts" ], 6 | "@/*": ["./src/*"] 7 | }, 8 | "lib": [ 9 | "es2022", 10 | "DOM", 11 | "DOM.Iterable" 12 | ] 13 | }, 14 | "extends": "@vue/tsconfig/tsconfig.dom.json" 15 | } -------------------------------------------------------------------------------- /client/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | import path from 'path' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [vue()], 8 | resolve: { 9 | alias: { 10 | "@": path.resolve(__dirname, "./src"), 11 | "@solaris-common": path.resolve(__dirname, "../common/src") 12 | }, 13 | }, 14 | build: { 15 | target: 'es2020', 16 | }, 17 | server: { 18 | port: 8080, 19 | }, 20 | preview: { 21 | port: 8080, 22 | }, 23 | envPrefix: 'VUE_APP' 24 | }) 25 | -------------------------------------------------------------------------------- /common/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solaris-common", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "test": "echo \"Error: no test specified\" && exit 1", 6 | "check": "tsc", 7 | "build": "tsc" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "description": "", 12 | "dependencies": {}, 13 | "devDependencies": {}, 14 | "files": [ 15 | "/dist" 16 | ], 17 | "main": "dist/index.js", 18 | "types": "dist/index.d.ts" 19 | } 20 | -------------------------------------------------------------------------------- /common/src/api/controllers/badge.ts: -------------------------------------------------------------------------------- 1 | import {GetRoute, PostRoute, SimpleGetRoute} from "./index"; 2 | import { type Badge} from "../types/common/badge"; 3 | import { type AwardedBadge } from "../types/common/user"; 4 | 5 | export type PurchaseForPlayerReq = { 6 | badgeKey: string; 7 | } 8 | 9 | export const createBadgeRoutes = () => ({ 10 | listAll: new SimpleGetRoute('/api/badges'), 11 | listForUser: new GetRoute<{userId: string}, {}, AwardedBadge[]>('/api/badges/user/:userId'), 12 | listForPlayer: new GetRoute<{ gameId: string, playerId: string }, {}, AwardedBadge[]>('/api/badges/game/:gameId/player/:playerId'), 13 | purchaseForPlayer: new PostRoute<{ gameId: string, playerId: string }, {}, PurchaseForPlayerReq, null>('/api/badges/game/:gameId/player/:playerId'), 14 | }); -------------------------------------------------------------------------------- /common/src/api/controllers/spectator.ts: -------------------------------------------------------------------------------- 1 | import {DeleteRoute, GetRoute, PutRoute} from "./index"; 2 | import {type GameSpectator} from "../types/common/game"; 3 | 4 | export const createSpectatorRoutes = () => ({ 5 | listSpectators: new GetRoute<{ gameId: string }, {}, GameSpectator[] | null>("/api/game/:gameId/spectators"), 6 | inviteSpectators: new PutRoute<{ gameId: string }, {}, { usernames: string[] }, {}>("/api/game/:gameId/spectators/invite"), 7 | uninviteSpectator: new PutRoute<{ gameId: string, userId: string }, {}, {}, {}>("/api/game/:gameId/spectators/uninvite/:userId"), 8 | clearSpectators: new DeleteRoute<{ gameId: string }, {}, {}>("/api/game/:gameId/spectators"), 9 | }); -------------------------------------------------------------------------------- /common/src/api/types/common/announcement.ts: -------------------------------------------------------------------------------- 1 | export type Announcement = { 2 | _id: ID, 3 | title: String, 4 | date: Date, 5 | content: String 6 | } 7 | 8 | export type AnnouncementState = { 9 | unreadAnnouncements: number; 10 | lastReadAnnouncement: ID; 11 | } 12 | -------------------------------------------------------------------------------- /common/src/api/types/common/avatar.ts: -------------------------------------------------------------------------------- 1 | export interface Avatar { 2 | id: number; 3 | file: string; 4 | name: string; 5 | description: string; 6 | price: number; 7 | isPatronAvatar: boolean; 8 | }; 9 | 10 | export interface UserAvatar extends Avatar { 11 | purchased: boolean; 12 | }; -------------------------------------------------------------------------------- /common/src/api/types/common/badge.ts: -------------------------------------------------------------------------------- 1 | export type Badge = { 2 | key: string; 3 | name: string; 4 | description: string; 5 | price: number; 6 | }; -------------------------------------------------------------------------------- /common/src/api/types/common/carrierWaypoint.ts: -------------------------------------------------------------------------------- 1 | export const CarrierWaypointActionTypes = ['nothing', 'collectAll', 'dropAll', 'collect', 'drop', 'collectAllBut', 'dropAllBut', 'dropPercentage', 'collectPercentage', 'garrison'] as const; 2 | 3 | export type CarrierWaypointActionType = typeof CarrierWaypointActionTypes[number]; 4 | 5 | export interface CarrierWaypointBase { 6 | source: ID; 7 | destination: ID; 8 | action: CarrierWaypointActionType; 9 | actionShips: number; 10 | delayTicks: number; 11 | }; 12 | 13 | export interface CarrierWaypoint extends CarrierWaypointBase { 14 | _id: ID; 15 | ticks?: number; 16 | ticksEta?: number; 17 | }; 18 | -------------------------------------------------------------------------------- /common/src/api/types/common/conversation.ts: -------------------------------------------------------------------------------- 1 | import type { ConversationMessage } from "./conversationMessage"; 2 | import type { TradeEvent } from "./trade"; 3 | 4 | export interface Conversation { 5 | _id: ID; 6 | participants: ID[]; 7 | createdBy: ID | null; 8 | name: string; 9 | mutedBy?: ID[]; 10 | messages: (ConversationMessage | TradeEvent)[]; 11 | lastMessage?: ConversationMessage; 12 | unreadCount?: number; 13 | isMuted?: boolean; 14 | }; 15 | -------------------------------------------------------------------------------- /common/src/api/types/common/conversationMessage.ts: -------------------------------------------------------------------------------- 1 | export interface ConversationMessage { 2 | _id?: ID; 3 | fromPlayerId: ID | null; 4 | fromPlayerAlias: string; 5 | message: string; 6 | sentDate: Date; 7 | sentTick: number | null; 8 | pinned: boolean; 9 | readBy: ID[]; 10 | type?: 'message'|'event'; 11 | }; 12 | 13 | export interface ConversationMessageSentResult extends ConversationMessage { 14 | conversationId: ID; 15 | toPlayerIds: ID[]; 16 | gameId: ID; 17 | gameName: string; 18 | }; 19 | -------------------------------------------------------------------------------- /common/src/api/types/common/diplomacy.ts: -------------------------------------------------------------------------------- 1 | export type DiplomaticState = 'enemies' | 'neutral' | 'allies'; 2 | 3 | export interface DiplomaticStatus { 4 | playerIdFrom: ID; 5 | playerIdTo: ID; 6 | playerFromAlias: string; 7 | playerToAlias: string; 8 | statusFrom: DiplomaticState; 9 | statusTo: DiplomaticState; 10 | actualStatus: DiplomaticState; 11 | }; 12 | 13 | export interface DiplomacyEvent { 14 | playerId: ID; 15 | type: string; 16 | data: DiplomaticStatus; 17 | sentDate: Date; 18 | sentTick: number; 19 | }; 20 | -------------------------------------------------------------------------------- /common/src/api/types/common/events/event.ts: -------------------------------------------------------------------------------- 1 | import type { StarCaptureResult } from "../star"; 2 | import type { CombatResult } from "../combat"; 3 | 4 | export interface BaseGameEvent { 5 | gameId: ID; 6 | tick: number; 7 | playerId: ID | null; 8 | type: string; 9 | read: boolean; 10 | date?: Date; 11 | }; 12 | 13 | export interface PlayerCombatStarEvent extends BaseGameEvent { 14 | type: 'playerCombatStar'; 15 | data: { 16 | playerIdOwner: ID; 17 | playerIdDefenders: ID[]; 18 | playerIdAttackers: ID[]; 19 | starId: ID; 20 | starName: string; 21 | captureResult: StarCaptureResult; 22 | combatResult: CombatResult; 23 | }; 24 | } -------------------------------------------------------------------------------- /common/src/api/types/common/guild.ts: -------------------------------------------------------------------------------- 1 | export type GuildDataForUser = { 2 | _id: ID; 3 | name: string; 4 | tag: string; 5 | }; -------------------------------------------------------------------------------- /common/src/api/types/common/ledger.ts: -------------------------------------------------------------------------------- 1 | export enum LedgerType { 2 | Credits = 'credits', 3 | CreditsSpecialists = 'creditsSpecialists' 4 | } -------------------------------------------------------------------------------- /common/src/api/types/common/location.ts: -------------------------------------------------------------------------------- 1 | export interface Location { 2 | x: number; 3 | y: number; 4 | }; 5 | -------------------------------------------------------------------------------- /common/src/api/types/common/map.ts: -------------------------------------------------------------------------------- 1 | import type { Location } from "./location"; 2 | 3 | export interface MapObject { 4 | _id: ID; 5 | ownedByPlayerId: ID | null; 6 | location: Location; 7 | }; 8 | 9 | export interface MapObjectWithVisibility extends MapObject { 10 | isAlwaysVisible?: boolean; 11 | }; 12 | -------------------------------------------------------------------------------- /common/src/api/types/common/subscriptions.ts: -------------------------------------------------------------------------------- 1 | export interface UserSubscriptions { 2 | settings: { 3 | notifyActiveGamesOnly: boolean; 4 | }, 5 | inapp?: { 6 | notificationsForOtherGames: boolean; 7 | }, 8 | discord?: { 9 | gameStarted: boolean; 10 | gameEnded: boolean; 11 | gameTurnEnded: boolean; 12 | playerGalacticCycleComplete: boolean; 13 | playerResearchComplete: boolean; 14 | playerTechnologyReceived: boolean; 15 | playerCreditsReceived: boolean; 16 | playerCreditsSpecialistsReceived: boolean; 17 | playerRenownReceived: boolean; 18 | conversationMessageSent: boolean; 19 | } 20 | } -------------------------------------------------------------------------------- /common/src/api/types/common/trade.ts: -------------------------------------------------------------------------------- 1 | import type { ResearchTypeNotRandom } from "./player"; 2 | 3 | export interface TradeTechnology { 4 | name: ResearchTypeNotRandom; 5 | level: number; 6 | cost: number; 7 | }; 8 | 9 | export interface TradeEvent { 10 | playerId: ID; 11 | type: string; 12 | data: any; 13 | sentDate: Date; 14 | sentTick: number; 15 | }; 16 | 17 | export interface TradeEventTechnology { 18 | name: string; 19 | level: number; 20 | difference: number; 21 | }; 22 | -------------------------------------------------------------------------------- /common/src/events/emitter.ts: -------------------------------------------------------------------------------- 1 | import { type EventName } from "./index"; 2 | 3 | export abstract class Emitter { 4 | protected eventType?: TEventType; 5 | 6 | constructor() { 7 | this.eventType; 8 | } 9 | 10 | // TData extends unknown is used to mitigate a known issue: https://github.com/microsoft/TypeScript/issues/30071 11 | protected abstract emit, TData extends unknown>(event: TEventName, data: TData): void; 12 | } 13 | -------------------------------------------------------------------------------- /common/src/events/handler.ts: -------------------------------------------------------------------------------- 1 | import { type EventName } from "./index"; 2 | 3 | export abstract class Handler { 4 | protected eventType?: TEventType; 5 | 6 | constructor() { 7 | this.eventType; 8 | } 9 | 10 | // TData extends unknown is used to mitigate a known issue: https://github.com/microsoft/TypeScript/issues/30071 11 | protected abstract on, TData extends unknown>(event: TEventName, listener: (e: TData) => void): void; 12 | } 13 | -------------------------------------------------------------------------------- /common/src/events/index.ts: -------------------------------------------------------------------------------- 1 | export type EventName = string & { eventType?: TEventType, data?: TData, eventName?: 'eventName' } -------------------------------------------------------------------------------- /common/src/sockets/socketEventNames/diplomacy.ts: -------------------------------------------------------------------------------- 1 | import { type DiplomaticStatus } from "../../api/types/common/diplomacy"; 2 | import { makeCastFunc } from "../../utilities/cast"; 3 | import {type EventName} from "../../events"; 4 | 5 | export type DiplomacySocketEventType = { diplomacySocketEventType: 'diplomacySocketEventType' }; 6 | export type DiplomacySocketEventName = EventName & { diplomacySocketEventName: 'diplomacySocketEventName' }; 7 | 8 | const toEventName: (value: string) => DiplomacySocketEventName = makeCastFunc(); 9 | 10 | export class DiplomacySocketEventNames { 11 | private constructor() { }; 12 | 13 | public static readonly PlayerDiplomaticStatusChanged: DiplomacySocketEventName<{ diplomaticStatus: DiplomaticStatus }> = toEventName('playerDiplomaticStatusChanged'); 14 | } -------------------------------------------------------------------------------- /common/src/sockets/socketEventNames/game.ts: -------------------------------------------------------------------------------- 1 | import { type GameState } from "../../api/types/common/game"; 2 | import { makeCastFunc } from "../../utilities/cast"; 3 | import { type EventName} from "../../events"; 4 | 5 | export type GameSocketEventType = { gameSocketEventType: 'gameSocketEventType' }; 6 | export type GameSocketEventName = EventName & { gameSocketEventName: 'gameSocketEventName' }; 7 | 8 | const toEventName: (value: string) => GameSocketEventName = makeCastFunc(); 9 | 10 | export class GameSocketEventNames { 11 | private constructor() { }; 12 | 13 | public static readonly GameStarted: GameSocketEventName<{ state: GameState }> = toEventName('gameStarted'); 14 | } -------------------------------------------------------------------------------- /common/src/sockets/socketEventNames/user.ts: -------------------------------------------------------------------------------- 1 | import type { EventName } from "../../events"; 2 | import {makeCastFunc} from "../../utilities/cast"; 3 | import type {ConversationMessageSentResult} from "../../api/types/common/conversationMessage"; 4 | 5 | export type UserSocketEventType = { UserSocketEventType: 'userSocketEventType' }; 6 | export type UserSocketEventName = EventName & { userSocketEventName: 'userSocketEventName' } 7 | 8 | const toEventName: (value: string) => UserSocketEventName = makeCastFunc(); 9 | 10 | export class UserSocketEventNames { 11 | public static readonly UserJoined: UserSocketEventName<{}> = toEventName("userJoined"); 12 | public static readonly GameMessageSent: UserSocketEventName> = toEventName('gameMessageSent'); 13 | public static readonly UserLeft: UserSocketEventName<{}> = toEventName("userLeft"); 14 | } -------------------------------------------------------------------------------- /common/src/utilities/cast.ts: -------------------------------------------------------------------------------- 1 | export function makeCastFunc(): (value: string) => T { 2 | return cast; 3 | } 4 | 5 | function cast(value: string): T { 6 | return value as T; 7 | } -------------------------------------------------------------------------------- /common/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "nodenext", 4 | "target": "es2019", 5 | "declaration": true, 6 | "outDir": "./dist", 7 | "strict": true 8 | }, 9 | "include": [ 10 | "src/**/*" 11 | ] 12 | } -------------------------------------------------------------------------------- /docker-compose.requirements.yml: -------------------------------------------------------------------------------- 1 | services: 2 | 3 | mongo: 4 | image: mongo:6 5 | ports: 6 | - "27017:27017" 7 | volumes: 8 | - "dbdata:/data/db" 9 | volumes: 10 | dbdata: 11 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | client: 3 | build: ./client 4 | ports: 5 | - "8080:8080" 6 | 7 | api: 8 | build: ./server 9 | command: npm run start-api:prod 10 | ports: 11 | - "3000:3000" 12 | depends_on: 13 | - database 14 | 15 | jobs: 16 | build: ./server 17 | command: npm run start-jobs:prod 18 | depends_on: 19 | - database 20 | 21 | database: 22 | image: mongo:6 23 | volumes: 24 | - ./database_data:/data/db 25 | ports: 26 | - "27017:27017" 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solaris", 3 | "scripts": { 4 | "build": "npm run build --workspaces --if-present", 5 | "check": "npm run check --workspaces --if-present" 6 | }, 7 | "workspaces": [ 8 | "common/", 9 | "client/", 10 | "server/" 11 | ], 12 | "devDependencies": { 13 | "typescript": "5.6.3" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pm2.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | apps: [ 3 | { 4 | name: "solaris-jobs", 5 | script: "npm", 6 | args: "run start-jobs:dev", 7 | cwd: "server", 8 | watch: false 9 | }, 10 | { 11 | name: "solaris-api", 12 | script: "npm", 13 | args: "run start-api:dev", 14 | cwd: "server", 15 | watch: false 16 | }, 17 | { 18 | name: "solaris-client", 19 | script: "npm run serve", 20 | cwd: "client", 21 | watch: false 22 | } 23 | ] 24 | }; 25 | -------------------------------------------------------------------------------- /server/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /server/.env.example: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | SESSION_SECRET=top_secret 3 | SESSION_SECURE_COOKIES=false 4 | CONNECTION_STRING=mongodb://127.0.0.1:27017/solaris 5 | CLIENT_URL=http://localhost:8080 6 | CLIENT_URL_ACCOUNT_SETTINGS=http://localhost:8080/#/account/settings 7 | SERVER_URL=http://localhost:3000 8 | SMTP_ENABLED=false 9 | SMTP_HOST=localhost 10 | SMTP_PORT=25 11 | SMTP_FROM=noreply@example.com 12 | CACHE_ENABLED=true 13 | PAYPAL_CLIENT_ID=ABC_123 14 | PAYPAL_CLIENT_SECRET=ABC_123 15 | PAYPAL_ENVIRONMENT=sandbox|production 16 | 17 | # Discord 18 | # if DISCORD_BOT_TOKEN is left empty, the discord bot service is not loaded 19 | DISCORD_BOT_TOKEN= 20 | DISCORD_SERVERID=... 21 | DISCORD_CLIENTID=... 22 | DISCORD_CLIENT_SECRET=... 23 | DISCORD_OAUTH_REDIRECT_URI=... 24 | -------------------------------------------------------------------------------- /server/.env_override_docker: -------------------------------------------------------------------------------- 1 | # Used by Dockerfile to override environment variables defined in .env 2 | CONNECTION_STRING=mongodb://database:27017/solaris 3 | -------------------------------------------------------------------------------- /server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package*.json ./ 6 | RUN npm install 7 | 8 | COPY . . 9 | RUN cat .env_override_docker >> .env 10 | 11 | RUN npm run build -------------------------------------------------------------------------------- /server/api/controllers/report.ts: -------------------------------------------------------------------------------- 1 | import { DependencyContainer } from '../../services/types/DependencyContainer'; 2 | import { mapToReportCreateReportRequest } from '../requests/report'; 3 | 4 | export default (container: DependencyContainer) => { 5 | return { 6 | create: async (req, res, next) => { 7 | try { 8 | const reqObj = mapToReportCreateReportRequest(req.body); 9 | 10 | await container.reportService.reportPlayer(req.game, reqObj, req.session.userId); 11 | 12 | res.sendStatus(200); 13 | return next(); 14 | } catch (err) { 15 | return next(err); 16 | } 17 | } 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /server/api/middleware/index.ts: -------------------------------------------------------------------------------- 1 | import { DependencyContainer } from "../../services/types/DependencyContainer"; 2 | import { AuthMiddleware, middleware as auth } from './auth'; 3 | import { CoreMiddleware, middleware as core } from './core'; 4 | import { GameMiddleware, middleware as game } from './game'; 5 | import { PlayerMiddleware, middleware as player } from './player'; 6 | import { PlayerMutexMiddleware, middleware as playerMutex } from "./playerMutex"; 7 | 8 | export interface MiddlewareContainer { 9 | core: CoreMiddleware, 10 | auth: AuthMiddleware, 11 | game: GameMiddleware, 12 | player: PlayerMiddleware, 13 | playerMutex: PlayerMutexMiddleware, 14 | }; 15 | 16 | export default (container: DependencyContainer) => { 17 | return { 18 | core: core(container), 19 | auth: auth(container), 20 | game: game(container), 21 | player: player(container), 22 | playerMutex: playerMutex(container) 23 | }; 24 | }; 25 | -------------------------------------------------------------------------------- /server/api/requests/admin.ts: -------------------------------------------------------------------------------- 1 | import * as Joi from 'joi'; 2 | 3 | export const adminSetUserRoleRequestSchema = Joi.object({ 4 | enabled: Joi.boolean().required() 5 | }); 6 | 7 | export const adminSetUserCreditsRequestSchema = Joi.object({ 8 | credits: Joi.number().required() 9 | }); 10 | 11 | export const adminSetGameFeaturedRequestSchema = Joi.object({ 12 | featured: Joi.boolean().required() 13 | }); 14 | 15 | export const adminSetGameTimeMachineRequestSchema = Joi.object({ 16 | timeMachine: Joi.string().required().valid('enabled', 'disabled') 17 | }); 18 | 19 | export const adminAddWarningRequestSchema = Joi.object({ 20 | text: Joi.string().required() 21 | }); -------------------------------------------------------------------------------- /server/api/requests/announcements.ts: -------------------------------------------------------------------------------- 1 | import {date, object, string, Validator} from "../validate"; 2 | 3 | export type AnnouncementRequest = { 4 | title: string; 5 | content: string; 6 | date: Date; 7 | }; 8 | 9 | export const parseAnnouncementRequest: Validator = object({ 10 | title: string, 11 | content: string, 12 | date: date 13 | }); -------------------------------------------------------------------------------- /server/api/requests/auth.ts: -------------------------------------------------------------------------------- 1 | import * as Joi from 'joi'; 2 | 3 | export const authLoginRequestSchema = Joi.object({ 4 | email: Joi.string().required().email().message('Email is required and must be a valid email address.'), 5 | password: Joi.string().required().messages({ 6 | 'string.empty': 'Password is required.' 7 | }) 8 | }); 9 | -------------------------------------------------------------------------------- /server/api/requests/badges.ts: -------------------------------------------------------------------------------- 1 | import * as Joi from 'joi'; 2 | 3 | export const badgesPurchaseBadgeRequestSchema = Joi.object({ 4 | badgeKey: Joi.string().required() 5 | }); 6 | -------------------------------------------------------------------------------- /server/api/requests/colour.ts: -------------------------------------------------------------------------------- 1 | import {object, string, Validator} from "../validate"; 2 | 3 | export type ColourOverrideRequest = { 4 | playerId: string; 5 | colour: { 6 | alias: string; 7 | value: string; 8 | } 9 | } 10 | 11 | export const parseColourOverrideRequest: Validator = object({ 12 | playerId: string, 13 | colour: object({ 14 | alias: string, 15 | value: string 16 | }) 17 | }); -------------------------------------------------------------------------------- /server/api/requests/spectator.ts: -------------------------------------------------------------------------------- 1 | import * as Joi from 'joi'; 2 | import {type Validator, array, string, object} from "../validate"; 3 | 4 | export const spectatorInviteSpectatorRequestSchema = Joi.object({ 5 | username: Joi.string().required().min(3).max(24) 6 | }); 7 | 8 | export type SpectatorInviteRequest = { 9 | usernames: string[]; 10 | } 11 | 12 | // TODO: Validate usernames 13 | export const parseSpectatorInviteRequest: Validator = object({ 14 | usernames: array(string), 15 | }); -------------------------------------------------------------------------------- /server/api/routes/colour.ts: -------------------------------------------------------------------------------- 1 | import {MiddlewareContainer} from "../middleware"; 2 | import {DependencyContainer} from "../../services/types/DependencyContainer"; 3 | import {SingleRouter} from "../singleRoute"; 4 | import ColourController from '../controllers/colour'; 5 | 6 | export default (router: SingleRouter, mw: MiddlewareContainer, container: DependencyContainer) => { 7 | const controller = ColourController(container); 8 | 9 | router.put('/api/game/:gameId/colour/override', 10 | mw.auth.authenticate(), 11 | mw.playerMutex.wait(), 12 | mw.game.loadGame({ 13 | lean: false, 14 | galaxy: true, 15 | }), 16 | mw.player.loadPlayer, 17 | mw.player.validatePlayerState({ isPlayerUndefeated: true }), 18 | controller.setColourOverride, 19 | mw.playerMutex.release() 20 | ); 21 | 22 | router.get('/api/colour/list', 23 | mw.auth.authenticate(), 24 | controller.list 25 | ); 26 | } -------------------------------------------------------------------------------- /server/api/routes/report.ts: -------------------------------------------------------------------------------- 1 | import { ExpressJoiInstance } from "express-joi-validation"; 2 | import { DependencyContainer } from "../../services/types/DependencyContainer"; 3 | import ReportController from '../controllers/report'; 4 | import { MiddlewareContainer } from "../middleware"; 5 | import { SingleRouter} from "../singleRoute"; 6 | 7 | export default (router: SingleRouter, mw: MiddlewareContainer, validator: ExpressJoiInstance, container: DependencyContainer) => { 8 | const controller = ReportController(container); 9 | 10 | router.post('/api/game/:gameId/report', 11 | mw.auth.authenticate(), 12 | mw.playerMutex.wait(), 13 | mw.game.loadGame({ 14 | lean: true, 15 | settings: true, 16 | 'galaxy.players': true 17 | }), 18 | mw.player.loadPlayer, 19 | controller.create, 20 | mw.playerMutex.release() 21 | ); 22 | 23 | return router; 24 | } -------------------------------------------------------------------------------- /server/api/routes/shop.ts: -------------------------------------------------------------------------------- 1 | import { ExpressJoiInstance } from "express-joi-validation"; 2 | import { DependencyContainer } from "../../services/types/DependencyContainer"; 3 | import ShopController from '../controllers/shop'; 4 | import { MiddlewareContainer } from "../middleware"; 5 | import {SingleRouter} from "../singleRoute"; 6 | 7 | export default (router: SingleRouter, mw: MiddlewareContainer, validator: ExpressJoiInstance, container: DependencyContainer) => { 8 | const controller = ShopController(container); 9 | 10 | router.get('/api/shop/galacticcredits/purchase', 11 | mw.auth.authenticate(), 12 | controller.purchase 13 | ); 14 | 15 | router.get('/api/shop/galacticcredits/purchase/process', 16 | mw.auth.authenticate(), 17 | controller.process 18 | ); 19 | 20 | return router; 21 | } -------------------------------------------------------------------------------- /server/config/game/specialStars.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "randomWormHoles", 4 | "name": "Worm Holes" 5 | }, 6 | { 7 | "id": "randomNebulas", 8 | "name": "Nebulas" 9 | }, 10 | { 11 | "id": "randomAsteroidFields", 12 | "name": "Asteroid Fields" 13 | }, 14 | { 15 | "id": "randomBlackHoles", 16 | "name": "Black Holes" 17 | }, 18 | { 19 | "id": "randomBinaryStars", 20 | "name": "Binary Stars" 21 | }, 22 | { 23 | "id": "randomPulsars", 24 | "name": "Pulsars" 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /server/config/types/Config.ts: -------------------------------------------------------------------------------- 1 | export type LoggingType = 'pretty' | 'stdout'; 2 | 3 | export interface Config { 4 | port?: string; 5 | sessionSecret?: string; 6 | sessionSecureCookies: boolean; 7 | connectionString?: string; 8 | serverUrl?: string; 9 | clientUrl?: string; 10 | corsUrls: string[]; 11 | cacheEnabled: boolean; 12 | logging?: LoggingType; 13 | logLevel?: string; 14 | smtp: { 15 | enabled: boolean; 16 | host?: string; 17 | port?: string; 18 | from?: string; 19 | }, 20 | paypal: { 21 | environment: string; 22 | clientId?: string; 23 | clientSecret?: string; 24 | }, 25 | discord: { 26 | serverId?: string; 27 | clientId?: string; 28 | clientSecret?: string; 29 | oauthRedirectUri?: string; 30 | botToken?: string; 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /server/db/models/Announcement.ts: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const mongooseLeanDefaults = require('mongoose-lean-defaults'); 3 | 4 | import schema from './schemas/announcement'; 5 | 6 | schema.plugin(mongooseLeanDefaults); 7 | 8 | const model = mongoose.model('announcement', schema); 9 | 10 | export default model; 11 | -------------------------------------------------------------------------------- /server/db/models/Event.ts: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const mongooseLeanDefaults = require('mongoose-lean-defaults'); 3 | 4 | import schema from './schemas/event'; 5 | 6 | schema.plugin(mongooseLeanDefaults); 7 | 8 | const model = mongoose.model('gameEvent', schema); 9 | 10 | export default model; 11 | -------------------------------------------------------------------------------- /server/db/models/Game.ts: -------------------------------------------------------------------------------- 1 | import mongoose = require('mongoose'); 2 | import mongooseLeanDefaults = require('mongoose-lean-defaults'); 3 | 4 | import schema from './schemas/game'; 5 | 6 | schema.plugin(mongooseLeanDefaults); 7 | 8 | const model = mongoose.model('game', schema); 9 | 10 | export default model; 11 | -------------------------------------------------------------------------------- /server/db/models/Guild.ts: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const mongooseLeanDefaults = require('mongoose-lean-defaults'); 3 | 4 | import schema from './schemas/guild'; 5 | 6 | schema.plugin(mongooseLeanDefaults); 7 | 8 | const model = mongoose.model('guild', schema); 9 | 10 | export default model; 11 | -------------------------------------------------------------------------------- /server/db/models/History.ts: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const mongooseLeanDefaults = require('mongoose-lean-defaults'); 3 | 4 | import schema from './schemas/history'; 5 | 6 | schema.plugin(mongooseLeanDefaults); 7 | 8 | const model = mongoose.model('gameHistory', schema); 9 | 10 | export default model; 11 | -------------------------------------------------------------------------------- /server/db/models/Payment.ts: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const mongooseLeanDefaults = require('mongoose-lean-defaults'); 3 | 4 | import schema from './schemas/payment'; 5 | 6 | schema.plugin(mongooseLeanDefaults); 7 | 8 | const model = mongoose.model('payment', schema); 9 | 10 | export default model; 11 | -------------------------------------------------------------------------------- /server/db/models/Report.ts: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const mongooseLeanDefaults = require('mongoose-lean-defaults'); 3 | 4 | import schema from './schemas/report'; 5 | 6 | schema.plugin(mongooseLeanDefaults); 7 | 8 | const model = mongoose.model('report', schema); 9 | 10 | export default model; 11 | -------------------------------------------------------------------------------- /server/db/models/User.ts: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const mongooseLeanDefaults = require('mongoose-lean-defaults'); 3 | 4 | import schema from './schemas/user'; 5 | 6 | schema.plugin(mongooseLeanDefaults); 7 | 8 | const model = mongoose.model('user', schema); 9 | 10 | export default model; 11 | -------------------------------------------------------------------------------- /server/db/models/schemas/action.ts: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | const Types = Schema.Types; 4 | 5 | const schema = new Schema({ 6 | _id: { type: Types.ObjectId, required: true }, // Actions are required to have an ObjectId so that we can refer to them via their ID instead of an array index, which may be unreliable. 7 | infrastructureType: { type: Types.String, required: true }, 8 | buyType: { type: Types.String, required: true }, 9 | amount: { type: Types.Number, required: true, default: 0, min: 0 }, 10 | repeat: { type: Types.Boolean, required: true, default: false }, 11 | tick: { type: Types.Number, required: true, default: 0, min: 0 } 12 | }); 13 | 14 | export default schema; -------------------------------------------------------------------------------- /server/db/models/schemas/announcement.ts: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | const Types = Schema.Types; 4 | 5 | 6 | const schema = new Schema({ 7 | _id: { type: Types.ObjectId, required: true }, 8 | date: { type: Types.Date, required: true, default: () => new Date() }, 9 | title: { type: Types.String, required: true }, 10 | content: { type: Types.String, required: true }, 11 | }); 12 | 13 | export default schema; -------------------------------------------------------------------------------- /server/db/models/schemas/badge.ts: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | const Types = Schema.Types; 4 | 5 | const schema = new Schema({ 6 | badge: { type: Types.String, required: true }, 7 | awardedBy: { type: Types.ObjectId, default: null }, 8 | awardedByName: { type: Types.String, default: null }, 9 | awardedInGame: { type: Types.ObjectId, default: null }, 10 | awardedInGameName: { type: Types.String, default: null }, 11 | playerAwarded: { type: Types.Boolean, default: false, required: true }, 12 | time: { type: Types.Date, required: false, default: null }, 13 | }); 14 | 15 | export default schema; -------------------------------------------------------------------------------- /server/db/models/schemas/conversation.ts: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | const Types = Schema.Types; 4 | 5 | import ConversationMessageSchema from './conversationMessage'; 6 | 7 | const schema = new Schema({ 8 | participants: [{ type: Types.ObjectId, required: true }], 9 | createdBy: { type: Types.ObjectId, required: false, default: null }, 10 | name: { type: Types.String, required: true }, 11 | mutedBy: [{ type: Types.ObjectId, required: true }], 12 | messages: [ConversationMessageSchema] 13 | }); 14 | 15 | export default schema; 16 | -------------------------------------------------------------------------------- /server/db/models/schemas/conversationMessage.ts: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | const Types = Schema.Types; 4 | 5 | const schema = new Schema({ 6 | fromPlayerId: { type: Types.ObjectId, required: false }, 7 | fromPlayerAlias: { type: Types.String, required: true }, 8 | message: { type: Types.String, required: true }, 9 | sentDate: { type: Types.Date, required: true }, 10 | sentTick: { type: Types.Number, required: false, default: null }, 11 | pinned: { type: Types.Boolean, required: false, default: false }, 12 | readBy: [{ type: Types.ObjectId, required: true }] 13 | }); 14 | 15 | export default schema; 16 | -------------------------------------------------------------------------------- /server/db/models/schemas/event.ts: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | const Types = Schema.Types; 4 | 5 | const schema = new Schema({ 6 | gameId: { type: Types.ObjectId, required: true }, 7 | playerId: { type: Types.ObjectId, required: false, default: null }, 8 | tick: { type: Types.Number, required: true }, 9 | type: { type: Types.String, required: true }, 10 | data: { type: Types.Mixed, required: true }, 11 | read: { type: Types.Boolean, required: false, default: false } 12 | }); 13 | 14 | schema.index({gameId: 1, tick: 1}, {unique: false}); 15 | schema.index({gameId: 1, playerId: 1}, {unique: false}); 16 | 17 | export default schema; 18 | -------------------------------------------------------------------------------- /server/db/models/schemas/guild.ts: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | const Types = Schema.Types; 4 | 5 | const schema = new Schema({ 6 | name: { type: Types.String, required: true, minlength: 4, maxlength: 31 }, 7 | tag: { type: Types.String, required: true, minlength: 2, maxlength: 4 }, 8 | leader: { type: Types.ObjectId, required: true }, 9 | officers: [{ type: Types.ObjectId }], 10 | members: [{ type: Types.ObjectId }], 11 | invitees: [{ type: Types.ObjectId }], 12 | applicants: [{ type: Types.ObjectId }] 13 | }); 14 | 15 | export default schema; 16 | -------------------------------------------------------------------------------- /server/db/models/schemas/payment.ts: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | const Types = Schema.Types; 4 | 5 | const schema = new Schema({ 6 | userId: { type: Types.ObjectId, required: true }, 7 | paymentId: { type: Types.String, required: true }, 8 | totalCost: { type: Types.Number, required: true }, 9 | totalQuantity: { type: Types.Number, required: true }, 10 | unitCost: { type: Types.Number, required: true } 11 | }); 12 | 13 | schema.index({paymentId: 1}, {unique: false}); 14 | 15 | export default schema; 16 | -------------------------------------------------------------------------------- /server/db/models/schemas/team.ts: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | const Types = Schema.Types; 4 | 5 | const schema = new Schema({ 6 | name: { type: Types.String, required: true }, 7 | players: [{ type: Types.ObjectId, required: true }], 8 | }); 9 | 10 | export default schema; -------------------------------------------------------------------------------- /server/db/models/schemas/waypoint.ts: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | const Types = Schema.Types; 4 | 5 | const schema = new Schema({ 6 | source: { type: Types.ObjectId, required: true }, 7 | destination: { type: Types.ObjectId, required: true }, 8 | action: { type: Types.String, required: true, enum: ['nothing', 'collectAll', 'dropAll', 'collect', 'drop', 'collectAllBut', 'dropAllBut', 'dropPercentage', 'collectPercentage', 'garrison'], default: 'nothing' }, 9 | actionShips: { type: Types.Number, required: true, default: 0 }, 10 | delayTicks: { type: Types.Number, required: true, default: 0 } 11 | }); 12 | 13 | export default schema; 14 | -------------------------------------------------------------------------------- /server/db/scripts/banSpecsInGame.txt: -------------------------------------------------------------------------------- 1 | // EXAMPLE CARRIER BAN 2 | db.games.updateMany({ 3 | 'state.endDate': { $eq: null }, 4 | 'settings.specialGalaxy.specialistBans.carrier': { $ne: null } 5 | }, { 6 | $addToSet: { 7 | 'settings.specialGalaxy.specialistBans.carrier': { 8 | $each: [ 19 ] 9 | } 10 | } 11 | }); 12 | 13 | 14 | 15 | // EXAMPLE STAR BAN 16 | db.games.updateMany({ 17 | 'state.endDate': { $eq: null }, 18 | 'settings.specialGalaxy.specialistBans.star': { $ne: null } 19 | }, { 20 | $addToSet: { 21 | 'settings.specialGalaxy.specialistBans.star': { 22 | $each: [ 18 ] 23 | } 24 | } 25 | }); -------------------------------------------------------------------------------- /server/db/scripts/clearGameQuitters.txt: -------------------------------------------------------------------------------- 1 | use solaris; 2 | 3 | let gameId = ObjectId('gameid'); 4 | 5 | db.games.updateOne({ 6 | _id: gameId 7 | }, { 8 | $set: { 9 | quitters: [] 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /server/db/scripts/deleteMessage.txt: -------------------------------------------------------------------------------- 1 | const mainDocumentId = ObjectId(''); 2 | const conversationId = ObjectId(''); 3 | const messageId = ObjectId(''); 4 | 5 | db.games.findOne({ 6 | _id: mainDocumentId, 7 | "conversations._id": conversationId 8 | }) 9 | 10 | db.games.updateOne( 11 | { 12 | _id: mainDocumentId, 13 | "conversations._id": conversationId 14 | }, 15 | { 16 | $pull: { "conversations.$.messages": { _id: messageId } } 17 | }); -------------------------------------------------------------------------------- /server/db/scripts/openPlayerSlot.txt: -------------------------------------------------------------------------------- 1 | use solaris; 2 | 3 | let playerId = ObjectId('66ca28d505b395f625e52f06'); 4 | 5 | db.games.updateOne({ 6 | 'galaxy.players._id': playerId 7 | }, { 8 | $set: { 9 | 'galaxy.players.$.afk': true, 10 | 'galaxy.players.$.defeated': true, 11 | 'galaxy.players.$.isOpenSlot': true 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /server/db/scripts/pauseInProgressGames.txt: -------------------------------------------------------------------------------- 1 | use solaris; 2 | 3 | // Pause 4 | db.games.updateMany({ 5 | $and: [ 6 | { 'state.paused': false }, 7 | { 'state.endDate': { $eq: null } }, 8 | { 'state.startDate': { $ne: null } } 9 | ] 10 | }, { 11 | $set: { 12 | 'state.paused': true 13 | } 14 | }); 15 | 16 | // Unpause 17 | db.games.updateMany({ 18 | $and: [ 19 | { 'state.paused': true }, 20 | { 'state.endDate': { $eq: null } }, 21 | { 'state.startDate': { $ne: null } } 22 | ] 23 | }, { 24 | $set: { 25 | 'state.paused': false 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /server/db/scripts/specialistHireStats.txt: -------------------------------------------------------------------------------- 1 | // playerCarrierSpecialistHired 2 | // playerStarSpecialistHired 3 | 4 | let type = 'playerCarrierSpecialistHired' 5 | 6 | db.gameevents.aggregate([ 7 | { 8 | $match: { 9 | type: { $in: [ type ] } 10 | } 11 | }, 12 | { 13 | $group: { 14 | _id: "$data.specialistId", 15 | totalHired: { $sum: 1 } 16 | } 17 | }, 18 | { 19 | $sort: { 20 | totalHired: 1 21 | } 22 | } 23 | ]) -------------------------------------------------------------------------------- /server/db/scripts/undefeatPlayer.txt: -------------------------------------------------------------------------------- 1 | use solaris; 2 | 3 | let playerId = ObjectId('playerid'); 4 | 5 | db.games.updateOne({ 6 | 'galaxy.players._id': playerId 7 | }, { 8 | $set: { 9 | 'galaxy.players.$.afk': false, 10 | 'galaxy.players.$.defeated': false, 11 | 'galaxy.players.$.defeatedDate': null, 12 | 'galaxy.players.$.ready': false, 13 | 'galaxy.players.$.readyToCycle': false, 14 | 'galaxy.players.$.readyToQuit': false, 15 | 'galaxy.players.$.missedTurns': 0, 16 | 'galaxy.players.$.hasSentTurnReminder': false, 17 | 'galaxy.players.$.lastSeen': new Date(), 18 | 'galaxy.players.$.lastSeenIP': null 19 | } 20 | }); 21 | -------------------------------------------------------------------------------- /server/db/scripts/whoIsPlayer.txt: -------------------------------------------------------------------------------- 1 | use solaris; 2 | 3 | let userId = db.games.findOne({ 4 | _id: ObjectId('676e1fa1b800f965b09fab1e'), 5 | 'galaxy.players.alias': 'Urineol' 6 | }, { 7 | 'galaxy.players.$': 1 8 | }).galaxy.players[0].userId; 9 | 10 | let username = db.users.findOne({ 11 | _id: userId 12 | }, { 13 | username: 1 14 | }).username; 15 | 16 | print(username); 17 | -------------------------------------------------------------------------------- /server/errors/validation.ts: -------------------------------------------------------------------------------- 1 | export default class ValidationError extends Error { 2 | statusCode: number; 3 | 4 | constructor(err: string | string[], statusCode?: number) { 5 | super(err as any); 6 | 7 | this.statusCode = statusCode || 400; 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /server/jobs/cleanupOldTutorials.ts: -------------------------------------------------------------------------------- 1 | import { DependencyContainer } from "../services/types/DependencyContainer"; 2 | import {logger} from "../utils/logging"; 3 | 4 | const log = logger("Cleanup Old Tutorials Job"); 5 | 6 | export const cleanupOldTutorialsJob = (container: DependencyContainer) => async () => { 7 | try { 8 | const games = await container.gameListService.listCompletedTutorials(); 9 | 10 | for (let i = 0; i < games.length; i++) { 11 | const game = games[i]; 12 | 13 | try { 14 | await container.gameService.delete(game); 15 | } catch (e) { 16 | log.error(e); 17 | } 18 | } 19 | } catch (e) { 20 | log.error(e, "CleanupOldTutorials job threw unhandled: " + e); 21 | } 22 | } -------------------------------------------------------------------------------- /server/jobs/scheduler/job.ts: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /server/services/cache.ts: -------------------------------------------------------------------------------- 1 | import { Config } from "../config/types/Config"; 2 | 3 | const cache = require('memory-cache'); 4 | 5 | export default class CacheService { 6 | 7 | config: Config; 8 | 9 | constructor(config: Config) { 10 | this.config = config; 11 | } 12 | 13 | put(key: string, obj, time: number) { 14 | if (this.config.cacheEnabled) { 15 | return cache.put(key, obj, time); 16 | } 17 | 18 | return null; 19 | } 20 | 21 | get(key: string) { 22 | if (this.config.cacheEnabled) { 23 | return cache.get(key); 24 | } 25 | 26 | return null; 27 | } 28 | 29 | }; 30 | -------------------------------------------------------------------------------- /server/services/gameAuth.ts: -------------------------------------------------------------------------------- 1 | import {Game} from "./types/Game"; 2 | import {DBObjectId} from "./types/DBObjectId"; 3 | import UserService from "./user"; 4 | 5 | export default class GameAuthService { 6 | userService: UserService; 7 | 8 | constructor(userService: UserService) { 9 | this.userService = userService; 10 | } 11 | 12 | async isGameAdmin(game: Game, userId: DBObjectId | null | undefined): Promise { 13 | if (!userId) { 14 | return false; 15 | } 16 | 17 | if (await this.userService.getUserIsAdmin(userId)) { 18 | return true; 19 | } 20 | 21 | if (game.settings.general.createdByUserId) { 22 | return game.settings.general.createdByUserId.toString() === userId.toString(); 23 | } 24 | 25 | return false; 26 | } 27 | } -------------------------------------------------------------------------------- /server/services/gameLock.ts: -------------------------------------------------------------------------------- 1 | import Repository from "./repository"; 2 | import {Game} from "./types/Game"; 3 | import {DBObjectId} from "./types/DBObjectId"; 4 | 5 | export default class GameLockService { 6 | gameRepo: Repository; 7 | 8 | constructor(gameRepo: Repository) { 9 | this.gameRepo = gameRepo; 10 | } 11 | 12 | async lock(gameId: DBObjectId, locked: boolean = true) { 13 | await this.gameRepo.updateOne({ 14 | _id: gameId 15 | }, { 16 | $set: { 17 | 'state.locked': locked 18 | } 19 | }); 20 | } 21 | 22 | async isLockedInDatabase(gameId: DBObjectId): Promise { 23 | const game = await this.gameRepo.findOne({ 24 | _id: gameId 25 | }, { 26 | state: { 27 | locked: 1 28 | } 29 | }); 30 | 31 | return Boolean(game?.state?.locked); 32 | } 33 | } -------------------------------------------------------------------------------- /server/services/password.ts: -------------------------------------------------------------------------------- 1 | export default class PasswordService { 2 | bcrypt; 3 | 4 | constructor( 5 | bcrypt 6 | ) { 7 | this.bcrypt = bcrypt; 8 | } 9 | 10 | async hash(password: string, length: number = 10) { 11 | return await this.bcrypt.hash(password, length); 12 | } 13 | 14 | async compare(password1: string, password2: string) { 15 | return await this.bcrypt.compare(password1, password2); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /server/services/types/Ai.ts: -------------------------------------------------------------------------------- 1 | export interface KnownAttack { 2 | arrivalTick: number; 3 | starId: string; 4 | carriersOnTheWay: string[]; 5 | } 6 | 7 | export interface InvasionInProgress { 8 | arrivalTick: number; 9 | star: string; 10 | } 11 | 12 | export interface AiState { 13 | knownAttacks: KnownAttack[]; 14 | invasionsInProgress: InvasionInProgress[]; 15 | startedClaims: string[]; 16 | } -------------------------------------------------------------------------------- /server/services/types/Announcement.ts: -------------------------------------------------------------------------------- 1 | import {DBObjectId} from "./DBObjectId"; 2 | 3 | export interface Announcement { 4 | _id: DBObjectId, 5 | title: String, 6 | date: Date, 7 | content: String 8 | } 9 | 10 | export interface AnnouncementState { 11 | unreadAnnouncements: number; 12 | lastReadAnnouncement: DBObjectId; 13 | } 14 | -------------------------------------------------------------------------------- /server/services/types/Avatar.ts: -------------------------------------------------------------------------------- 1 | export interface Avatar { 2 | id: number; 3 | file: string; 4 | name: string; 5 | description: string; 6 | price: number; 7 | isPatronAvatar: boolean; 8 | }; 9 | 10 | export interface UserAvatar extends Avatar { 11 | purchased: boolean; 12 | }; 13 | -------------------------------------------------------------------------------- /server/services/types/Badge.ts: -------------------------------------------------------------------------------- 1 | export interface Badge { 2 | key: string; 3 | name: string; 4 | description: string; 5 | price: number; 6 | }; -------------------------------------------------------------------------------- /server/services/types/CarrierWaypoint.ts: -------------------------------------------------------------------------------- 1 | import { DBObjectId } from "./DBObjectId"; 2 | 3 | export const CarrierWaypointActionTypes = ['nothing', 'collectAll', 'dropAll', 'collect', 'drop', 'collectAllBut', 'dropAllBut', 'dropPercentage', 'collectPercentage', 'garrison'] as const; 4 | 5 | export type CarrierWaypointActionType = typeof CarrierWaypointActionTypes[number]; 6 | 7 | export interface CarrierWaypointBase { 8 | source: DBObjectId; 9 | destination: DBObjectId; 10 | action: CarrierWaypointActionType; 11 | actionShips: number; 12 | delayTicks: number; 13 | }; 14 | 15 | export interface CarrierWaypoint extends CarrierWaypointBase { 16 | _id: DBObjectId; 17 | ticks?: number; 18 | ticksEta?: number; 19 | }; 20 | -------------------------------------------------------------------------------- /server/services/types/Conversation.ts: -------------------------------------------------------------------------------- 1 | import { ConversationMessage } from "./ConversationMessage"; 2 | import { DBObjectId } from "./DBObjectId"; 3 | import { TradeEvent } from "./Trade"; 4 | 5 | export interface Conversation { 6 | _id: DBObjectId; 7 | participants: DBObjectId[]; 8 | createdBy: DBObjectId | null; 9 | name: string; 10 | mutedBy?: DBObjectId[]; 11 | messages: (ConversationMessage | TradeEvent)[]; 12 | 13 | lastMessage?: ConversationMessage; 14 | unreadCount?: number; 15 | isMuted?: boolean; 16 | }; 17 | -------------------------------------------------------------------------------- /server/services/types/ConversationMessage.ts: -------------------------------------------------------------------------------- 1 | import { DBObjectId } from "./DBObjectId"; 2 | 3 | export interface ConversationMessage { 4 | _id?: DBObjectId; 5 | fromPlayerId: DBObjectId | null; 6 | fromPlayerAlias: string; 7 | message: string; 8 | sentDate: Date; 9 | sentTick: number | null; 10 | pinned: boolean; 11 | readBy: DBObjectId[]; 12 | 13 | type?: 'message'|'event'; 14 | }; 15 | 16 | export interface ConversationMessageSentResult extends ConversationMessage { 17 | conversationId: DBObjectId; 18 | toPlayerIds: DBObjectId[]; 19 | gameId: DBObjectId; 20 | gameName: string; 21 | }; 22 | -------------------------------------------------------------------------------- /server/services/types/DBObjectId.ts: -------------------------------------------------------------------------------- 1 | import mongoose, { ObjectId } from "mongoose"; 2 | 3 | export interface DBObjectId extends mongoose.Types.ObjectId { 4 | // equals(id: DBObjectId): boolean; -- Note: We never use this as we cannot ensure that anything that comes through the API layer via params are mongo object IDs unless we explicitly cast them. 5 | getTimestamp(): Date; 6 | toString(): string; 7 | }; 8 | 9 | export const objectId = (): DBObjectId => new mongoose.Types.ObjectId() as any; 10 | 11 | export const objectIdFromString = (s: string): DBObjectId => mongoose.Types.ObjectId(s) as any; -------------------------------------------------------------------------------- /server/services/types/Dimensions.ts: -------------------------------------------------------------------------------- 1 | export interface GalaxyDimensions { 2 | minX: number; 3 | minY: number; 4 | maxX: number; 5 | maxY: number; 6 | }; 7 | -------------------------------------------------------------------------------- /server/services/types/Diplomacy.ts: -------------------------------------------------------------------------------- 1 | import { DBObjectId } from "./DBObjectId"; 2 | 3 | export type DiplomaticState = 'enemies' | 'neutral' | 'allies'; 4 | 5 | export interface DiplomaticStatus { 6 | playerIdFrom: DBObjectId; 7 | playerIdTo: DBObjectId; 8 | playerFromAlias: string; 9 | playerToAlias: string; 10 | statusFrom: DiplomaticState; 11 | statusTo: DiplomaticState; 12 | actualStatus: DiplomaticState; 13 | }; 14 | 15 | export interface DiplomacyEvent { 16 | playerId: DBObjectId; 17 | type: string; 18 | data: DiplomaticStatus; 19 | sentDate: Date; 20 | sentTick: number; 21 | }; 22 | -------------------------------------------------------------------------------- /server/services/types/Email.ts: -------------------------------------------------------------------------------- 1 | export interface EmailTemplate { 2 | fileName: string; 3 | subject: string; 4 | }; 5 | -------------------------------------------------------------------------------- /server/services/types/Flux.ts: -------------------------------------------------------------------------------- 1 | export interface Flux { 2 | id: number; 3 | name: string; 4 | month: string; 5 | description: string; 6 | tooltip: string; 7 | }; 8 | -------------------------------------------------------------------------------- /server/services/types/GameEvent.ts: -------------------------------------------------------------------------------- 1 | import { DBObjectId } from "./DBObjectId"; 2 | 3 | export interface GameEvent { 4 | _id: DBObjectId; 5 | gameId: DBObjectId; 6 | playerId: DBObjectId | null; 7 | tick: number; 8 | type: string; 9 | data; 10 | read: boolean; 11 | 12 | date?: Date; 13 | }; 14 | -------------------------------------------------------------------------------- /server/services/types/GameMutexLock.ts: -------------------------------------------------------------------------------- 1 | import { MutexLock } from "./MutexLock"; 2 | 3 | export interface GameMutexLock extends MutexLock { 4 | gameId: string 5 | } -------------------------------------------------------------------------------- /server/services/types/GameTick.ts: -------------------------------------------------------------------------------- 1 | import { Carrier } from "./Carrier"; 2 | import { CarrierWaypoint } from "./CarrierWaypoint"; 3 | import { Star } from "./Star"; 4 | 5 | export interface CarrierActionWaypoint { 6 | carrier: Carrier; 7 | star: Star; 8 | waypoint: CarrierWaypoint; 9 | }; 10 | -------------------------------------------------------------------------------- /server/services/types/Location.ts: -------------------------------------------------------------------------------- 1 | export interface Location { 2 | x: number; 3 | y: number; 4 | }; 5 | -------------------------------------------------------------------------------- /server/services/types/Map.ts: -------------------------------------------------------------------------------- 1 | import { DBObjectId } from "./DBObjectId"; 2 | import { Location } from "./Location"; 3 | import { Player } from "./Player"; 4 | 5 | export interface MapObject { 6 | _id: DBObjectId; 7 | ownedByPlayerId: DBObjectId | null; 8 | location: Location; 9 | }; 10 | 11 | export interface MapObjectWithVisibility extends MapObject { 12 | isAlwaysVisible?: boolean; 13 | }; 14 | -------------------------------------------------------------------------------- /server/services/types/MutexLock.ts: -------------------------------------------------------------------------------- 1 | export interface MutexLock { 2 | key: string, 3 | mutexLockId: number 4 | } -------------------------------------------------------------------------------- /server/services/types/Payment.ts: -------------------------------------------------------------------------------- 1 | import { DBObjectId } from "./DBObjectId"; 2 | 3 | export interface Payment { 4 | _id: DBObjectId; 5 | userId: DBObjectId; 6 | paymentId: string; 7 | totalCost: number; 8 | totalQuantity: number; 9 | unitCost: number; 10 | }; 11 | -------------------------------------------------------------------------------- /server/services/types/PlayerMutexLock.ts: -------------------------------------------------------------------------------- 1 | import { MutexLock } from "./MutexLock"; 2 | 3 | export interface PlayerMutexLock extends MutexLock { 4 | playerId: string 5 | } -------------------------------------------------------------------------------- /server/services/types/Rating.ts: -------------------------------------------------------------------------------- 1 | import { DBObjectId } from "./DBObjectId"; 2 | 3 | export interface EloRatingChange { 4 | _id: DBObjectId; 5 | newRating: number; 6 | oldRating: number; 7 | }; 8 | 9 | export interface EloRatingChangeResult { 10 | winner: EloRatingChange; 11 | loser: EloRatingChange; 12 | }; 13 | 14 | export interface GameRanking { 15 | playerId: DBObjectId; 16 | current: number; 17 | new: number; 18 | }; 19 | 20 | export interface GameRankingResult { 21 | ranks: GameRanking[]; 22 | eloRating: EloRatingChangeResult | null; 23 | }; 24 | -------------------------------------------------------------------------------- /server/services/types/Report.ts: -------------------------------------------------------------------------------- 1 | import { DBObjectId } from "./DBObjectId"; 2 | 3 | export interface ReportReasons { 4 | abuse: boolean; 5 | spamming: boolean; 6 | multiboxing: boolean; 7 | inappropriateAlias: boolean; 8 | }; 9 | 10 | export interface Report { 11 | _id: DBObjectId; 12 | gameId: DBObjectId; 13 | reportedPlayerId: DBObjectId; 14 | reportedUserId: DBObjectId; 15 | reportedPlayerAlias: string; 16 | reportedByPlayerId: DBObjectId; 17 | reportedByUserId: DBObjectId; 18 | reportedByPlayerAlias: string; 19 | reportedConversationId: DBObjectId | null; 20 | reportedMessageId: DBObjectId | null; 21 | reasons: ReportReasons; 22 | actioned: boolean; 23 | actionedBy: DBObjectId | null; 24 | date: Date; 25 | }; 26 | -------------------------------------------------------------------------------- /server/services/types/Request.ts: -------------------------------------------------------------------------------- 1 | import { Game } from "./Game"; 2 | import { Player } from "./Player"; 3 | import { PlayerMutexLock } from "./PlayerMutexLock"; 4 | 5 | export { }; 6 | 7 | declare global { 8 | namespace Express { 9 | interface Request { 10 | game?: Game; 11 | player?: Player; 12 | playerMutexLocks?: PlayerMutexLock[]; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /server/services/types/SessionData.ts: -------------------------------------------------------------------------------- 1 | import 'express-session' 2 | import { DBObjectId } from './DBObjectId' 3 | import { UserRoles } from './User'; 4 | 5 | declare module 'express-session' { 6 | interface SessionData { 7 | userId: DBObjectId; 8 | username: string; 9 | roles: UserRoles; 10 | userCredits: number; 11 | isImpersonating: boolean; 12 | originalUserId?: DBObjectId; 13 | } 14 | } -------------------------------------------------------------------------------- /server/services/types/SpecialStar.ts: -------------------------------------------------------------------------------- 1 | export type SpecialStarType = 2 | 'randomWormHoles' | 3 | 'randomNebulas' | 4 | 'randomAsteroidFields' | 5 | 'randomBlackHoles' | 6 | 'randomBinaryStars' | 7 | 'randomPulsars'; 8 | 9 | export interface SpecialStar { 10 | id: SpecialStarType, 11 | name: string; 12 | }; 13 | -------------------------------------------------------------------------------- /server/services/types/Trade.ts: -------------------------------------------------------------------------------- 1 | import { DBObjectId } from "./DBObjectId"; 2 | import { ResearchTypeNotRandom } from "./Player"; 3 | 4 | export interface TradeTechnology { 5 | name: ResearchTypeNotRandom; 6 | level: number; 7 | cost: number; 8 | }; 9 | 10 | export interface TradeEvent { 11 | playerId: DBObjectId; 12 | type: string; 13 | data; 14 | sentDate: Date; 15 | sentTick: number; 16 | }; 17 | 18 | export interface TradeEventTechnology { 19 | name: string; 20 | level: number; 21 | difference: number; 22 | }; 23 | -------------------------------------------------------------------------------- /server/services/types/Tutorial.ts: -------------------------------------------------------------------------------- 1 | export type TutorialLevel = 'beginner' | 'intermediate' | 'advanced'; 2 | 3 | export interface Tutorial { 4 | key: string; 5 | file: string; 6 | name: string; 7 | description: string; 8 | level: TutorialLevel; 9 | completed?: boolean; 10 | }; 11 | -------------------------------------------------------------------------------- /server/services/types/UserLevel.ts: -------------------------------------------------------------------------------- 1 | export interface UserLevel { 2 | id: number; 3 | name: string; 4 | rankPoints: number; 5 | rankPointsNext?: number | null; 6 | rankPointsProgress?: number | null; 7 | }; 8 | -------------------------------------------------------------------------------- /server/services/types/events/BaseGameEvent.ts: -------------------------------------------------------------------------------- 1 | import { DBObjectId } from "../DBObjectId"; 2 | 3 | export interface BaseGameEvent { 4 | gameId: DBObjectId; 5 | gameTick: number; 6 | playerId?: DBObjectId; 7 | }; 8 | -------------------------------------------------------------------------------- /server/services/types/events/ConversationMessageSent.ts: -------------------------------------------------------------------------------- 1 | import { Conversation } from "../Conversation"; 2 | import { ConversationMessageSentResult } from "../ConversationMessage"; 3 | import { BaseGameEvent } from "./BaseGameEvent"; 4 | 5 | export default interface ConversationMessageSentEvent extends BaseGameEvent { 6 | conversation: Conversation, 7 | sentMessageResult: ConversationMessageSentResult 8 | }; 9 | -------------------------------------------------------------------------------- /server/services/types/events/GameDiplomacyPeaceDeclared.ts: -------------------------------------------------------------------------------- 1 | import { DiplomaticStatus } from "../Diplomacy"; 2 | import { BaseGameEvent } from "./BaseGameEvent"; 3 | 4 | export default interface GameDiplomacyPeaceDeclaredEvent extends BaseGameEvent { 5 | status: DiplomaticStatus; 6 | }; 7 | -------------------------------------------------------------------------------- /server/services/types/events/GameDiplomacyWarDeclared.ts: -------------------------------------------------------------------------------- 1 | import { DiplomaticStatus } from "../Diplomacy"; 2 | import { BaseGameEvent } from "./BaseGameEvent"; 3 | 4 | export default interface GameDiplomacyWarDeclaredEvent extends BaseGameEvent { 5 | status: DiplomaticStatus; 6 | }; 7 | -------------------------------------------------------------------------------- /server/services/types/events/GameEnded.ts: -------------------------------------------------------------------------------- 1 | import { GameRankingResult } from "../Rating"; 2 | import { BaseGameEvent } from "./BaseGameEvent"; 3 | 4 | export default interface GameEndedEvent extends BaseGameEvent { 5 | rankingResult: GameRankingResult | null; 6 | }; 7 | -------------------------------------------------------------------------------- /server/services/types/events/GamePlayerAFK.ts: -------------------------------------------------------------------------------- 1 | import { BaseGameEvent } from "./BaseGameEvent"; 2 | 3 | export default interface GamePlayerAFKEvent extends BaseGameEvent { 4 | playerAlias: string; 5 | }; 6 | -------------------------------------------------------------------------------- /server/services/types/events/GamePlayerBadgePurchased.ts: -------------------------------------------------------------------------------- 1 | import { DBObjectId } from "../DBObjectId"; 2 | import { BaseGameEvent } from "./BaseGameEvent"; 3 | 4 | export default interface GamePlayerBadgePurchasedEvent extends BaseGameEvent { 5 | purchasedByPlayerId: DBObjectId; 6 | purchasedByPlayerAlias: string; 7 | purchasedForPlayerId: DBObjectId; 8 | purchasedForPlayerAlias: string; 9 | badgeKey: string; 10 | badgeName: string; 11 | }; 12 | -------------------------------------------------------------------------------- /server/services/types/events/GamePlayerDefeated.ts: -------------------------------------------------------------------------------- 1 | import { BaseGameEvent } from "./BaseGameEvent"; 2 | 3 | export default interface GamePlayerDefeatedEvent extends BaseGameEvent { 4 | playerAlias: string; 5 | openSlot: boolean; 6 | }; 7 | -------------------------------------------------------------------------------- /server/services/types/events/GamePlayerJoined.ts: -------------------------------------------------------------------------------- 1 | import { BaseGameEvent } from "./BaseGameEvent"; 2 | 3 | export default interface GamePlayerJoinedEvent extends BaseGameEvent { 4 | playerAlias: string; 5 | } -------------------------------------------------------------------------------- /server/services/types/events/GamePlayerQuit.ts: -------------------------------------------------------------------------------- 1 | import { BaseGameEvent } from "./BaseGameEvent"; 2 | 3 | export default interface GamePlayerQuitEvent extends BaseGameEvent { 4 | playerAlias: string; 5 | }; 6 | -------------------------------------------------------------------------------- /server/services/types/events/GameTurnEnded.ts: -------------------------------------------------------------------------------- 1 | import { BaseGameEvent } from "./BaseGameEvent"; 2 | 3 | export default interface GameTurnEndedEvent extends BaseGameEvent { }; 4 | -------------------------------------------------------------------------------- /server/services/types/events/PlayerGalacticCycleComplete.ts: -------------------------------------------------------------------------------- 1 | import { BaseGameEvent } from "./BaseGameEvent"; 2 | 3 | export default interface PlayerGalacticCycleCompleteEvent extends BaseGameEvent { 4 | creditsEconomy: number; 5 | creditsBanking: number; 6 | creditsSpecialists: number; 7 | experimentTechnology: string | null; 8 | experimentTechnologyLevel: number | null; 9 | experimentAmount: number | null; 10 | experimentLevelUp: boolean | null; 11 | experimentResearchingNext: string | null; 12 | carrierUpkeep: { 13 | carrierCount: number; 14 | totalCost: number 15 | } | null; 16 | allianceUpkeep: { 17 | allianceCount: number; 18 | totalCost: number; 19 | } | null; 20 | }; 21 | -------------------------------------------------------------------------------- /server/sockets/serverStub.ts: -------------------------------------------------------------------------------- 1 | import type { Server } from "socket.io"; 2 | 3 | export const serverStub = { on: (...args: any[]) => { }, engine: { on: (...args: any[]) => { } } } as unknown as Server; 4 | -------------------------------------------------------------------------------- /server/sockets/socketEmitters/diplomacy.ts: -------------------------------------------------------------------------------- 1 | import { Server } from "socket.io"; 2 | import { DiplomaticStatus } from "solaris-common"; 3 | import { DiplomacySocketEventNames, DiplomacySocketEventType } from "solaris-common"; 4 | import { ServerSocketEmitter } from "./serverSocketEmitter"; 5 | 6 | export class DiplomacyServerSocketEmitter extends ServerSocketEmitter { 7 | constructor(server: Server) { 8 | super(server); 9 | } 10 | 11 | public emitPlayerDiplomaticStatusChanged(room: string | string[], data: { diplomaticStatus: DiplomaticStatus }) { 12 | this.emit(room, DiplomacySocketEventNames.PlayerDiplomaticStatusChanged, data); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /server/sockets/socketEmitters/game.ts: -------------------------------------------------------------------------------- 1 | import { Server } from "socket.io"; 2 | import { GameState } from "solaris-common"; 3 | import { GameSocketEventNames, GameSocketEventType } from "solaris-common"; 4 | import { ServerSocketEmitter } from "./serverSocketEmitter"; 5 | 6 | export class GameServerSocketEmitter extends ServerSocketEmitter { 7 | constructor(server: Server) { 8 | super(server); 9 | } 10 | 11 | public emitGameStarted(room: string | string[], data: { state: GameState }) { 12 | this.emit(room, GameSocketEventNames.GameStarted, data); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /server/sockets/socketEmitters/user.ts: -------------------------------------------------------------------------------- 1 | import {ServerSocketEmitter} from "./serverSocketEmitter"; 2 | import { 3 | ConversationMessageSentResult, 4 | UserSocketEventNames, 5 | UserSocketEventType 6 | } from "solaris-common"; 7 | 8 | export class UserServerSocketEmitter extends ServerSocketEmitter { 9 | public emitGameMessageSent(room: string | string[], data: ConversationMessageSentResult) { 10 | this.emit(room, UserSocketEventNames.GameMessageSent, data); 11 | } 12 | } -------------------------------------------------------------------------------- /server/sockets/socketEventNames/server.ts: -------------------------------------------------------------------------------- 1 | import { Socket } from "socket.io"; 2 | import { type EventName } from "solaris-common"; 3 | import { makeCastFunc } from "solaris-common"; 4 | 5 | export type ServerSocketEventType = { serverSocketEventType: 'serverSocketEventType' }; 6 | export type ServerSocketEventName = EventName & { serverSocketEventName: 'serverSocketEventName' }; 7 | 8 | const toEventName: (value: string) => ServerSocketEventName = makeCastFunc(); 9 | 10 | export default class ServerSocketEventNames { 11 | private constructor() { }; 12 | 13 | public static readonly Connection: ServerSocketEventName = toEventName('connection'); 14 | public static readonly ConnectionError: ServerSocketEventName = toEventName('connection_error'); 15 | } -------------------------------------------------------------------------------- /server/sockets/socketHandlers/serverSocketHandler.ts: -------------------------------------------------------------------------------- 1 | import {type EventName, Handler} from "solaris-common"; 2 | import { ServerHandler } from "./serverHandler"; 3 | 4 | export abstract class ServerSocketHandler extends Handler { 5 | constructor(private serverHandler: ServerHandler) { 6 | super(); 7 | this.eventType; 8 | } 9 | 10 | protected override on, TData>(event: TEventName, listener: (e: TData) => void) { 11 | const eventName = event as string; 12 | 13 | this.serverHandler.register(eventName, listener); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/spec/gameList.spec.ts: -------------------------------------------------------------------------------- 1 | import GameListService from '../services/gameList'; 2 | 3 | const fakeGameModel = { 4 | find() { 5 | return 1; 6 | } 7 | }; 8 | 9 | describe('game list', () => { 10 | let gameListService; 11 | 12 | beforeAll(() => { 13 | // @ts-ignore 14 | gameListService = new GameListService(fakeGameModel); 15 | }); 16 | 17 | // Not really sure what's the point in testing this. Maybe there's a better way to mock 18 | // and intercept calling of mongoose functions? 19 | it('should list official games', async () => { 20 | let result = await gameListService.listOfficialGames(); 21 | 22 | expect(result).toBe(1); 23 | }); 24 | 25 | }); 26 | -------------------------------------------------------------------------------- /server/spec/support/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "spec", 3 | "spec_files": [ 4 | "**/*[sS]pec.ts" 5 | ], 6 | "helpers": [ 7 | "helpers/**/*.ts" 8 | ], 9 | "stopSpecOnExpectationFailure": false, 10 | "random": false 11 | } 12 | -------------------------------------------------------------------------------- /server/tools/migrate.ts: -------------------------------------------------------------------------------- 1 | import {makeJob} from "./tool"; 2 | import {migrateBadges} from "./migrations/migrateBadges"; 3 | import {migrateCombatResolution} from "./migrations/migrateCombatResolution"; 4 | 5 | const MIGRATIONS = { 6 | "2025-02-05-badges": migrateBadges, 7 | "2025-03-29-combat-resolution": migrateCombatResolution 8 | } 9 | 10 | const job = makeJob('Migration', async (ctx) => { 11 | const migrationName = process.argv[2]; 12 | 13 | if (!migrationName || !MIGRATIONS[migrationName]) { 14 | console.error('No migration name provided. Valid names are:', Object.keys(MIGRATIONS).join(', ')); 15 | 16 | throw new Error('No migration name provided'); 17 | } 18 | 19 | console.log(`Running migration ${migrationName}...`); 20 | 21 | const migration = MIGRATIONS[migrationName]; 22 | 23 | await migration(ctx); 24 | 25 | console.log(`Migration ${migrationName} done.`); 26 | }); 27 | 28 | job(); 29 | 30 | export {}; -------------------------------------------------------------------------------- /server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2016", 4 | "module": "commonjs", 5 | "resolveJsonModule": true, 6 | "sourceMap": true, 7 | "outDir": "./dist", 8 | "noEmit": false, 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "strict": true, 12 | "noImplicitAny": false, 13 | "skipLibCheck": true, 14 | "allowJs": true, 15 | "paths": { 16 | "@solaris-common": ["../common/src/index.ts"] 17 | } 18 | }, 19 | "include": [ 20 | "**/*.ts", 21 | "db/migrations/*.js", 22 | "config/**/*.json", 23 | "services/emailTemplates/*.html" 24 | ] 25 | } 26 | --------------------------------------------------------------------------------