WIP: graphql-ui #34
10
.drone.yml
Normal file
10
.drone.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
kind: pipeline
|
||||
name: default
|
||||
steps:
|
||||
- name: test
|
||||
image: crystallang/crystal:latest-alpine
|
||||
environment:
|
||||
commands:
|
||||
- make deps
|
||||
- make build
|
||||
- make test
|
26
README.md
26
README.md
|
@ -1,6 +1,6 @@
|
|||
# TETU Core
|
||||
|
||||
A strategy & simulation game in space, inspired by Stellaris PDX
|
||||
A strategy & simulation game in space, inspired by Stellaris PDX.
|
||||
|
||||
[![Build Status](https://drone.sceptique.eu/api/badges/TETU/Core/status.svg)](https://drone.sceptique.eu/TETU/Core)
|
||||
|
||||
|
@ -10,25 +10,21 @@ Install `git`, `sfml`, `crystal`, `make`, `imgui` (`imgui-sfml` with archlinux).
|
|||
|
||||
# install dependencies first
|
||||
make deps
|
||||
|
||||
# make with imgui static linking (I think, I don't remember)
|
||||
export LD_LIBRARY_PATH="$(pwd)/cimgui"
|
||||
make release
|
||||
|
||||
# make an optimised build so it's fasteeer
|
||||
make release
|
||||
|
||||
## Usage
|
||||
|
||||
# run the
|
||||
export LD_LIBRARY_PATH="$(pwd)/lib/imgui-sfml"
|
||||
./core
|
||||
|
||||
# there is also a make rule that handle the libraries
|
||||
make run
|
||||
./core # run the server (high performance for data)
|
||||
xdg-open https://localhost:3000 # open the game UI (browser UI for simplicity)
|
||||
|
||||
## Development
|
||||
|
||||
* See the wiki <https://git.sceptique.eu/TETU/Core/wiki>.
|
||||
* See the current kanban: <https://git.sceptique.eu/TETU/Core/projects>
|
||||
* Come talk on IRC **irc://irc.sceptique.eu#TETU**
|
||||
* See the wiki for documentation: <https://git.sceptique.eu/TETU/Core/wiki>.
|
||||
* See the current kanban for current WIP: <https://git.sceptique.eu/TETU/Core/projects>.
|
||||
* Come talk on IRC: **irc://irc.sceptique.eu:6697#TETU**.
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
|
@ -40,7 +36,7 @@ Install `git`, `sfml`, `crystal`, `make`, `imgui` (`imgui-sfml` with archlinux).
|
|||
|
||||
## Contributors
|
||||
|
||||
- [Arthur Poulet](https://git.sceptique.eu/Sceptique) - creator and maintainer
|
||||
- [Arthur Poulet](https://git.sceptique.eu/Sceptique) - creator and maintainer.
|
||||
|
||||
## Particular mentions
|
||||
|
||||
|
|
19
adr/0001-record-architecture-decisions.md
Normal file
19
adr/0001-record-architecture-decisions.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
# 1. Record architecture decisions
|
||||
|
||||
Date: 2022-08-07
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
We need to record the architectural decisions made on this project.
|
||||
|
||||
## Decision
|
||||
|
||||
We will use Architecture Decision Records, as [described by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions).
|
||||
|
||||
## Consequences
|
||||
|
||||
See Michael Nygard's article, linked above. For a lightweight ADR toolset, see Nat Pryce's [adr-tools](https://github.com/npryce/adr-tools).
|
23
adr/0002-use-a-ecs.md
Normal file
23
adr/0002-use-a-ecs.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
# 2. Use a ECS
|
||||
|
||||
Date: 2022-08-07
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
One of the target of the game is to be able to run 10.000 planets galaxy on a standard computer. This may require a very careful approch of the architecture. Naive implementation will very likely ends up with non-scalable galaxy size and limited planet amount.
|
||||
|
||||
## Decision
|
||||
|
||||
An ECS pattern will be used to handle most of the code. In particular the economic system that handle the planets.
|
||||
|
||||
The ECS is entitas.cr (see the shards.yml file).
|
||||
|
||||
## Consequences
|
||||
|
||||
Developer(s) need to know this unusual pattern.
|
||||
Performance are expected very high and scalable with threads.
|
||||
The architecture of most of the code will be defined and limited by the ECS pattern.
|
5
graphql/base.graphql
Normal file
5
graphql/base.graphql
Normal file
|
@ -0,0 +1,5 @@
|
|||
type Query {
|
||||
empires(name: String!): String!
|
||||
|
||||
day(): Int!
|
||||
}
|
26
shard.lock
26
shard.lock
|
@ -1,20 +1,28 @@
|
|||
version: 2.0
|
||||
shards:
|
||||
crsfml:
|
||||
git: https://github.com/oprypin/crsfml.git
|
||||
version: 2.5.2
|
||||
backtracer:
|
||||
git: https://github.com/sija/backtracer.cr.git
|
||||
version: 1.2.1
|
||||
|
||||
entitas:
|
||||
git: https://github.com/spoved/entitas.cr.git
|
||||
version: 1.4.5
|
||||
|
||||
imgui:
|
||||
git: https://github.com/oprypin/crystal-imgui.git
|
||||
version: 1.87
|
||||
exception_page:
|
||||
git: https://github.com/crystal-loot/exception_page.git
|
||||
version: 0.2.2
|
||||
|
||||
imgui-sfml:
|
||||
git: https://github.com/oprypin/crystal-imgui-sfml.git
|
||||
version: 1.87
|
||||
graphql:
|
||||
git: https://github.com/graphql-crystal/graphql.git
|
||||
version: 0.4.0
|
||||
|
||||
kemal:
|
||||
git: https://github.com/kemalcr/kemal.git
|
||||
version: 1.2.0
|
||||
|
||||
radix:
|
||||
git: https://github.com/luislavena/radix.git
|
||||
version: 0.4.1
|
||||
|
||||
spoved:
|
||||
git: https://github.com/spoved/spoved.cr.git
|
||||
|
|
10
shard.yml
10
shard.yml
|
@ -15,9 +15,7 @@ license: GPLv3
|
|||
dependencies:
|
||||
entitas:
|
||||
github: spoved/entitas.cr
|
||||
crsfml:
|
||||
github: oprypin/crsfml
|
||||
imgui:
|
||||
github: oprypin/crystal-imgui
|
||||
imgui-sfml:
|
||||
github: oprypin/crystal-imgui-sfml
|
||||
graphql:
|
||||
github: graphql-crystal/graphql
|
||||
kemal:
|
||||
github: kemalcr/kemal
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
require "./spec_helper"
|
||||
|
||||
describe Core do
|
||||
# TODO: Write tests
|
||||
|
||||
describe TETU do
|
||||
it "works" do
|
||||
false.should eq(true)
|
||||
true.should eq(true)
|
||||
end
|
||||
end
|
||||
|
|
4
src/api.cr
Normal file
4
src/api.cr
Normal file
|
@ -0,0 +1,4 @@
|
|||
module TETU::API
|
||||
end
|
||||
|
||||
require "./api/*"
|
21
src/api/game_query.cr
Normal file
21
src/api/game_query.cr
Normal file
|
@ -0,0 +1,21 @@
|
|||
require "./objects"
|
||||
|
||||
module TETU::API::Definitions
|
||||
@[GraphQL::Object]
|
||||
class GameQuery < GraphQL::BaseQuery
|
||||
@[GraphQL::Field]
|
||||
def hello(name : String) : String
|
||||
"Hello, #{name}!"
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def empire : Empire
|
||||
Empire.new
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def planet(id : ID? = nil) : Planet
|
||||
Planet.new(id: id)
|
||||
end
|
||||
end
|
||||
end
|
50
src/api/http_server.cr
Normal file
50
src/api/http_server.cr
Normal file
|
@ -0,0 +1,50 @@
|
|||
class TETU::API::HttpServer
|
||||
def self.start
|
||||
spawn do
|
||||
Kemal.run do |config|
|
||||
server = config.server.not_nil!
|
||||
server.bind_tcp(
|
||||
host: ENV.fetch("HOST", "127.0.0.1"),
|
||||
port: ENV.fetch("PORT", "3000").to_i,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require "./game_query"
|
||||
|
||||
schema = GraphQL::Schema.new(TETU::API::Definitions::GameQuery.new)
|
||||
|
||||
|
||||
# class CustomHandler < Kemal::Handler
|
||||
# only ["/graphql"], "POST"
|
||||
|
||||
# def call(context)
|
||||
# puts "Doing some custom stuff here"
|
||||
# spawn do
|
||||
# puts "Async stuff finished now"
|
||||
# call_next context
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# add_handler CustomHandler.new
|
||||
|
||||
module TETU::API
|
||||
PLAYER_CHANNEL = Channel(Tuple(String, Channel(String))).new
|
||||
RESPONSE_CHANNEL = Channel(String).new
|
||||
end
|
||||
|
||||
post "/graphql" do |env|
|
||||
env.response.content_type = "application/json"
|
||||
|
||||
query = env.params.json["query"].as(String)
|
||||
variables = env.params.json["variables"]?.as(Hash(String, JSON::Any)?)
|
||||
operation_name = env.params.json["operationName"]?.as(String?)
|
||||
context = TETU::API::Definitions::PlayerContext.new(TETU::API::PLAYER_CHANNEL)
|
||||
schema.execute(query, variables, operation_name, context)
|
||||
end
|
||||
|
||||
get "/" do |env|
|
||||
"<html></html>"
|
||||
end
|
3
src/api/objects.cr
Normal file
3
src/api/objects.cr
Normal file
|
@ -0,0 +1,3 @@
|
|||
require "./objects/base"
|
||||
require "./objects/planet"
|
||||
require "./objects/all"
|
48
src/api/objects/all.cr
Normal file
48
src/api/objects/all.cr
Normal file
|
@ -0,0 +1,48 @@
|
|||
require "./base"
|
||||
require "./planet"
|
||||
|
||||
module TETU::API::Definitions
|
||||
@[GraphQL::Object]
|
||||
class Star < GraphQL::BaseObject
|
||||
@@i : ID = 0
|
||||
|
||||
def initialize(id : Int32? = nil)
|
||||
if id.nil?
|
||||
@id = @@i
|
||||
@@i += 1
|
||||
else
|
||||
@id = id
|
||||
end
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
getter id : ID
|
||||
|
||||
@[GraphQL::Field]
|
||||
def name : String
|
||||
"my #{@id}th star name"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module TETU::API::Definitions
|
||||
@[GraphQL::Object]
|
||||
class Empire < GraphQL::BaseObject
|
||||
@[GraphQL::Field]
|
||||
def name : String
|
||||
"Player"
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def stars(context) : Array(Star)
|
||||
context.player_channel.send({ "stars", TETU::API::RESPONSE_CHANNEL })
|
||||
answer = TETU::API::RESPONSE_CHANNEL.receive
|
||||
answer.split(",").map { |name| Star.new }
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def planets : Array(Planet)
|
||||
[] of Planet
|
||||
end
|
||||
end
|
||||
end
|
8
src/api/objects/base.cr
Normal file
8
src/api/objects/base.cr
Normal file
|
@ -0,0 +1,8 @@
|
|||
module TETU::API::Definitions
|
||||
alias ID = Int32
|
||||
|
||||
class PlayerContext < GraphQL::Context
|
||||
def initialize(@player_channel : Channel(Tuple(String, Channel(String))))
|
||||
end
|
||||
end
|
||||
end
|
23
src/api/objects/planet.cr
Normal file
23
src/api/objects/planet.cr
Normal file
|
@ -0,0 +1,23 @@
|
|||
module TETU::API::Definitions
|
||||
@[GraphQL::Object]
|
||||
class Planet < GraphQL::BaseObject
|
||||
@@i : ID = 0
|
||||
|
||||
def initialize(id : Int32? = nil)
|
||||
if id.nil?
|
||||
@id = @@i
|
||||
@@i += 1
|
||||
else
|
||||
@id = id
|
||||
end
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
getter id : ID
|
||||
|
||||
@[GraphQL::Field]
|
||||
def name : String
|
||||
"my #{@id}th planet name"
|
||||
end
|
||||
end
|
||||
end
|
33
src/core.cr
33
src/core.cr
|
@ -1,24 +1,20 @@
|
|||
# require "log"
|
||||
require "yaml"
|
||||
|
||||
require "entitas"
|
||||
require "crsfml"
|
||||
require "imgui"
|
||||
require "imgui-sfml"
|
||||
require "graphql"
|
||||
require "spoved/logger"
|
||||
require "kemal"
|
||||
|
||||
module TETU
|
||||
spoved_logger level: :info, io: STDOUT, bind: true
|
||||
|
||||
# TO BE USED
|
||||
# TODO: TO BE USED
|
||||
module Systems
|
||||
# Log = TETU::Log.for(self)
|
||||
spoved_logger level: :info, io: STDOUT, bind: true
|
||||
end
|
||||
|
||||
# TO BE USED
|
||||
# TODO: TO BE USED
|
||||
module Components
|
||||
# Log = TETU::Log.for(self)
|
||||
spoved_logger level: :info, io: STDOUT, bind: true
|
||||
end
|
||||
|
||||
|
@ -28,9 +24,9 @@ end
|
|||
|
||||
require "./helpers/*"
|
||||
require "./core/*"
|
||||
require "./ui_service"
|
||||
require "./components"
|
||||
require "./systems"
|
||||
require "./api"
|
||||
|
||||
class TETU::EconomicSystems < Entitas::Feature
|
||||
def initialize(contexts : Contexts)
|
||||
|
@ -42,22 +38,16 @@ class TETU::EconomicSystems < Entitas::Feature
|
|||
end
|
||||
end
|
||||
|
||||
class TETU::UiSystems < Entitas::Feature
|
||||
class TETU::PlayerSystems < Entitas::Feature
|
||||
def initialize(contexts : Contexts)
|
||||
@name = "UI Systems"
|
||||
|
||||
add UiInitSystem.new(contexts)
|
||||
add UiBackgroundSystem.new(contexts)
|
||||
add UiEmpireSystem.new(contexts)
|
||||
add UiPlanetSystem.new(contexts)
|
||||
add UiDrawSystem.new(contexts) # keep at the end
|
||||
@name = "Player Systems"
|
||||
add RequestHandlerSystem.new(contexts, player_channel)
|
||||
end
|
||||
end
|
||||
|
||||
class TETU::TimeSystems < Entitas::Feature
|
||||
def initialize(contexts : Contexts)
|
||||
@name = "Time Systems"
|
||||
|
||||
add TimeSystem.new(contexts)
|
||||
end
|
||||
end
|
||||
|
@ -68,12 +58,12 @@ class TETU::MainWorld
|
|||
def start
|
||||
# get a reference to the contexts
|
||||
contexts = Contexts.shared_instance
|
||||
|
||||
# create the systems by creating individual features
|
||||
@systems = Entitas::Feature.new("systems")
|
||||
.add(TimeSystems.new(contexts))
|
||||
.add(EconomicSystems.new(contexts))
|
||||
.add(UiSystems.new(contexts))
|
||||
.add(PlayerSystems.new(contexts))
|
||||
# .add(UiSystems.new(contexts))
|
||||
@systems.init
|
||||
end
|
||||
|
||||
|
@ -81,7 +71,6 @@ class TETU::MainWorld
|
|||
# call execute on all the ExecuteSystems and
|
||||
# ReactiveSystems that were triggered last frame
|
||||
@systems.execute
|
||||
|
||||
# call cleanup on all the CleanupSystems
|
||||
@systems.cleanup
|
||||
end
|
||||
|
@ -115,9 +104,11 @@ module TETU
|
|||
t2 = Time.local
|
||||
logger.debug { "Duration: #{t2 - t1}" }
|
||||
logger.debug { "" }
|
||||
sleep 0.1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
TETU::API::HttpServer.start
|
||||
TETU.main_loop
|
||||
|
|
|
@ -1,33 +1,33 @@
|
|||
require "./configuration"
|
||||
|
||||
class TETU::Window
|
||||
@@instance = Window.new
|
||||
# class TETU::Window
|
||||
# @@instance = Window.new
|
||||
|
||||
def self.instance
|
||||
@@instance
|
||||
end
|
||||
# def self.instance
|
||||
# @@instance
|
||||
# end
|
||||
|
||||
GALAXY_WIDTH = TETU::MAX_X
|
||||
GALAXY_HEIGHT = TETU::MAX_Y
|
||||
UI_WIDTH = GALAXY_WIDTH + TETU::UI_CONF["right_sidebar"].as_i64
|
||||
UI_HEIGHT = GALAXY_HEIGHT
|
||||
SQUARE_SIZE = TETU::UI_CONF["square_size"].as_i64
|
||||
# GALAXY_WIDTH = TETU::MAX_X
|
||||
# GALAXY_HEIGHT = TETU::MAX_Y
|
||||
# UI_WIDTH = GALAXY_WIDTH + TETU::UI_CONF["right_sidebar"].as_i64
|
||||
# UI_HEIGHT = GALAXY_HEIGHT
|
||||
# SQUARE_SIZE = TETU::UI_CONF["square_size"].as_i64
|
||||
|
||||
GALAXY = SF::Texture.from_file("assets/#{GALAXY_WIDTH}x#{GALAXY_HEIGHT}/galaxy.jpg")
|
||||
# GALAXY = SF::Texture.from_file("assets/#{GALAXY_WIDTH}x#{GALAXY_HEIGHT}/galaxy.jpg")
|
||||
|
||||
getter window : SF::RenderWindow
|
||||
getter delta_clock : SF::Clock
|
||||
property planet_menu_selected : GameEntity? = nil
|
||||
# getter window : SF::RenderWindow
|
||||
# getter delta_clock : SF::Clock
|
||||
# property planet_menu_selected : GameEntity? = nil
|
||||
|
||||
def initialize
|
||||
@window = SF::RenderWindow.new(
|
||||
SF::VideoMode.new(UI_WIDTH, UI_HEIGHT),
|
||||
"To the End of The Universe",
|
||||
)
|
||||
@delta_clock = SF::Clock.new
|
||||
end
|
||||
# def initialize
|
||||
# @window = SF::RenderWindow.new(
|
||||
# SF::VideoMode.new(UI_WIDTH, UI_HEIGHT),
|
||||
# "To the End of The Universe",
|
||||
# )
|
||||
# @delta_clock = SF::Clock.new
|
||||
# end
|
||||
|
||||
def [](k)
|
||||
@data[k]
|
||||
end
|
||||
end
|
||||
# def [](k)
|
||||
# @data[k]
|
||||
# end
|
||||
# end
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
require "./systems/*"
|
||||
require "./systems/ui/*"
|
||||
require "./systems/player/*"
|
||||
|
|
20
src/systems/player/request_handler.cr
Normal file
20
src/systems/player/request_handler.cr
Normal file
|
@ -0,0 +1,20 @@
|
|||
class TETU::RequestHandlerSystem
|
||||
include Entitas::Systems::ExecuteSystem
|
||||
spoved_logger level: :info, io: STDOUT, bind: true
|
||||
|
||||
getter player_channel : Channel(Tuple(String, Channel(String)))
|
||||
@contexts : Contexts
|
||||
|
||||
def initialize(@contexts, @player_channel)
|
||||
end
|
||||
|
||||
def execute
|
||||
message, responder = player_channel.receive
|
||||
if message == "stars"
|
||||
stars = @contexts.game.get_group Entitas::Matcher.all_of(Named, Position, CelestialBody, PlayerOwned).none_of(StellarPosition)
|
||||
responder.send stars.entities.map { |entity| entity.named.name }.join(",")
|
||||
else
|
||||
responder.send ""
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user