From bd8a2b1bd351e728597555d9789c38ecca0ce781 Mon Sep 17 00:00:00 2001 From: Thomas Cat Date: Wed, 7 Aug 2024 10:59:41 +0800 Subject: [PATCH] [cardcity] service drafts --- src/func.xyz/cardcity/cards/card.go | 120 +++++++++++++++++++++ src/func.xyz/cardcity/cards/dummy.yaml | 60 +++++++++++ src/func.xyz/cardcity/cards/embed.go | 47 ++++++++ src/func.xyz/cardcity/cmd/cardcity-gate/main.go | 7 -- src/func.xyz/cardcity/cmd/cardcity-updater/main.go | 7 -- src/func.xyz/cardcity/cmd/cc-castle/main.go | 28 +++++ src/func.xyz/cardcity/cmd/cc-gateway/main.go | 28 +++++ .../cmd/{cardcity-cron => cc-worker}/main.go | 0 src/func.xyz/cardcity/etc/config.go | 43 -------- .../cardcity/internal/api/castle/handler.go | 37 +++++++ .../cardcity/internal/api/castle/service_states.go | 8 ++ .../cardcity/internal/api/gateway/handler.go | 37 +++++++ .../internal/api/gateway/service_states.go | 8 ++ .../cardcity/internal/coheartbeat/heartbeat.go | 9 ++ .../cardcity/internal/dao/{city.go => castle.go} | 0 src/func.xyz/cardcity/internal/dao/db.go | 2 +- src/func.xyz/cardcity/internal/dao/world_info.go | 40 +++++++ .../cardcity/internal/worker/castle_updater.go | 1 + .../cardcity/internal/worker/collision_resolver.go | 1 + src/func.xyz/cardcity/internal/worker/worker.go | 1 + .../cardcity/internal/worker/world_updater.go | 1 + src/func.xyz/cardcity/web/index.html | 6 -- 22 files changed, 427 insertions(+), 64 deletions(-) create mode 100644 src/func.xyz/cardcity/cards/card.go create mode 100644 src/func.xyz/cardcity/cards/dummy.yaml create mode 100644 src/func.xyz/cardcity/cards/embed.go delete mode 100644 src/func.xyz/cardcity/cmd/cardcity-gate/main.go delete mode 100644 src/func.xyz/cardcity/cmd/cardcity-updater/main.go create mode 100644 src/func.xyz/cardcity/cmd/cc-castle/main.go create mode 100644 src/func.xyz/cardcity/cmd/cc-gateway/main.go rename src/func.xyz/cardcity/cmd/{cardcity-cron => cc-worker}/main.go (100%) delete mode 100644 src/func.xyz/cardcity/etc/config.go create mode 100644 src/func.xyz/cardcity/internal/api/castle/handler.go create mode 100644 src/func.xyz/cardcity/internal/api/castle/service_states.go create mode 100644 src/func.xyz/cardcity/internal/api/gateway/handler.go create mode 100644 src/func.xyz/cardcity/internal/api/gateway/service_states.go create mode 100644 src/func.xyz/cardcity/internal/coheartbeat/heartbeat.go rename src/func.xyz/cardcity/internal/dao/{city.go => castle.go} (100%) create mode 100644 src/func.xyz/cardcity/internal/dao/world_info.go create mode 100644 src/func.xyz/cardcity/internal/worker/castle_updater.go create mode 100644 src/func.xyz/cardcity/internal/worker/collision_resolver.go create mode 100644 src/func.xyz/cardcity/internal/worker/worker.go create mode 100644 src/func.xyz/cardcity/internal/worker/world_updater.go delete mode 100644 src/func.xyz/cardcity/web/index.html diff --git a/src/func.xyz/cardcity/cards/card.go b/src/func.xyz/cardcity/cards/card.go new file mode 100644 index 0000000..fc495be --- /dev/null +++ b/src/func.xyz/cardcity/cards/card.go @@ -0,0 +1,120 @@ +package cards + +import ( + "slices" + "strings" +) + +type CardScript struct { + + /******* Basic *******/ + + Name string `yaml:"name"` + Description string `yaml:"description"` + ImageURL string `yaml:"image-url"` + // is able to move or be moved by military + Mobile bool `yaml:"mobile"` + + // global identifiable, the prefix is type + ID string `yaml:"-"` + // land consumption of the holding castle + LandConsumptions int `yaml:"land-consumptions"` + // recipes to produce this card + Recipes []cardRecipe `yaml:"recipes"` + + /******* Optional *******/ + + BattleAttrs *cardBattleAttrs `yaml:"battle-attrs"` +} + +type cardRecipe struct { + // if there are more than one recipe got the same details but different + // results, randomly generating the result card by weight. + Weight int `yaml:"weight"` + // demanded resources + Resources []struct { + ID string `yaml:"id"` + // when producing other card, the probability of being remained, + // omitted means 0. + RemainingPercent int `yaml:"remaining-percent"` + } `yaml:"resources"` +} + +func (recipe *cardRecipe) String() string { + ids := make([]string, len(recipe.Resources)) + for i, res := range recipe.Resources { + ids[i] = res.ID + } + return recipeString(ids...) +} + +type cardBattleAttrs struct { + Attack int `yaml:"attack"` + Defence int `yaml:"defence"` + // possible value, combat, piercing and ranged + WeaponType string `yaml:"weapon-type"` + // possible value, combat, piercing and ranged + ArmorType string `yaml:"armor-type"` +} + +func recipeString(resIDs ...string) string { + idBuilder := strings.Builder{} + slices.Sort(resIDs) + for _, resID := range resIDs { + idBuilder.WriteString(resID) + idBuilder.WriteString("\x00") + } + return idBuilder.String() +} + +var ( + scripts = map[string]*CardScript{} + recipes = map[string][]Recipe{} +) + +type Recipe struct { + cardRecipe + Result string +} + +func registerCardScript(script *CardScript) { + if _, has := scripts[script.ID]; has { + panic("duplicated card id") + } + scripts[script.ID] = script + for _, recipe := range script.Recipes { + str := recipe.String() + ls := recipes[str] + ls = append(ls, Recipe{ + cardRecipe: recipe, + Result: script.ID, + }) + recipes[str] = ls + } +} + +func MatchRecipe(resIDs []string, rand int) (Recipe, bool) { + + ls, ok := recipes[recipeString(resIDs...)] + if !ok { + return Recipe{}, false + } + + weights := 0 + for _, recipe := range ls { + weights += recipe.Weight + } + rand = rand % weights + for { + for _, recipe := range ls { + rand -= recipe.Weight + if rand <= 0 { + return recipe, true + } + } + } +} + +func GetCardScript(id string) *CardScript { + return scripts[id] +} diff --git a/src/func.xyz/cardcity/cards/dummy.yaml b/src/func.xyz/cardcity/cards/dummy.yaml new file mode 100644 index 0000000..f51acb8 --- /dev/null +++ b/src/func.xyz/cardcity/cards/dummy.yaml @@ -0,0 +1,60 @@ +# Copyright (c) 2024 Jack J. Mao All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +%YAML 1.2 +--- + + +######## Basic ######## + + +# frontend display name +name: demo card + +# description detail, also included invisible attributes in human language +description: | + we can describe those player invisible attributes here, + with human language... + blabla, blue blue + yoho yoho!!! + +# frontend card image +image-url: https://example.org/demo-card.jpeg + +# is able to move or be moved by military +mobile: true + +# global identifiable, see [ID & Types] +id: demo-card + +# land consumption of the holding castle +land-consumption: 3 + +# recipes to produce this card +recipes: + + # demanded resources + - resources: + - id: resource-card-01 + - id: resource-card-02 + # when producing other card, the probability of being destroyed, + # omitted means 1. + remaining-percent: 1 + - id: resource-card-01 + remaining-percent: 0.1 + # if there are more than one recipes got the same details but different + # result, randomly generating the result card by weight. + weight: 12 + + - resources: + - id: resource-card-big + remaining-percent: 0 + + +######## Optional ######## + + +# the battle related attributes in one object, omitted means null, +# see [Battle Attributes] +battle-attrs: null diff --git a/src/func.xyz/cardcity/cards/embed.go b/src/func.xyz/cardcity/cards/embed.go new file mode 100644 index 0000000..91f00cf --- /dev/null +++ b/src/func.xyz/cardcity/cards/embed.go @@ -0,0 +1,47 @@ +// Copyright (c) 2024 Jack J. Mao All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cards + +import ( + "embed" + "path/filepath" + "strings" + + "gopkg.in/yaml.v3" +) + +//go:embed *.yaml +var resFS embed.FS + +func init() { + + entries, err := resFS.ReadDir("/") + if err != nil { + panic(err) + } + + for _, entry := range entries { + + if !entry.Type().IsRegular() { + continue + } + + filename := entry.Name() + file, err := resFS.Open("/" + filename) + if err != nil { + panic(err) + } + + script := &CardScript{} + err = yaml.NewDecoder(file).Decode(script) + script.ID = strings.TrimSuffix(filename, filepath.Ext(filename)) + file.Close() + if err != nil { + panic(err) + } + + registerCardScript(script) + } +} diff --git a/src/func.xyz/cardcity/cmd/cardcity-gate/main.go b/src/func.xyz/cardcity/cmd/cardcity-gate/main.go deleted file mode 100644 index 551fa33..0000000 --- a/src/func.xyz/cardcity/cmd/cardcity-gate/main.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) 2024 Jack J. Mao All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func main() {} diff --git a/src/func.xyz/cardcity/cmd/cardcity-updater/main.go b/src/func.xyz/cardcity/cmd/cardcity-updater/main.go deleted file mode 100644 index 551fa33..0000000 --- a/src/func.xyz/cardcity/cmd/cardcity-updater/main.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) 2024 Jack J. Mao All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func main() {} diff --git a/src/func.xyz/cardcity/cmd/cc-castle/main.go b/src/func.xyz/cardcity/cmd/cc-castle/main.go new file mode 100644 index 0000000..b52d86c --- /dev/null +++ b/src/func.xyz/cardcity/cmd/cc-castle/main.go @@ -0,0 +1,28 @@ +// Copyright (c) 2024 Jack J. Mao All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "net/http" + + "func.xyz/cardcity/internal/api/castle" + "github.com/spf13/pflag" +) + +func main() { + + addr := pflag.StringP("addr", "A", ":8721", "http serving address") + pflag.Parse() + + handler := api.New() + server := &http.Server{ + Addr: *addr, + Handler: handler, + } + err := server.ListenAndServe() + if err != nil { + panic(err) + } +} diff --git a/src/func.xyz/cardcity/cmd/cc-gateway/main.go b/src/func.xyz/cardcity/cmd/cc-gateway/main.go new file mode 100644 index 0000000..21c86c7 --- /dev/null +++ b/src/func.xyz/cardcity/cmd/cc-gateway/main.go @@ -0,0 +1,28 @@ +// Copyright (c) 2024 Jack J. Mao All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "net/http" + + "func.xyz/cardcity/internal/api/castle" + "github.com/spf13/pflag" +) + +func main() { + + addr := pflag.StringP("addr", "A", ":8088", "http serving address") + pflag.Parse() + + handler := api.New() + server := &http.Server{ + Addr: *addr, + Handler: handler, + } + err := server.ListenAndServe() + if err != nil { + panic(err) + } +} diff --git a/src/func.xyz/cardcity/cmd/cardcity-cron/main.go b/src/func.xyz/cardcity/cmd/cc-worker/main.go similarity index 100% rename from src/func.xyz/cardcity/cmd/cardcity-cron/main.go rename to src/func.xyz/cardcity/cmd/cc-worker/main.go diff --git a/src/func.xyz/cardcity/etc/config.go b/src/func.xyz/cardcity/etc/config.go deleted file mode 100644 index 84315aa..0000000 --- a/src/func.xyz/cardcity/etc/config.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2024 Jack J. Mao All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package etc - -import ( - "strconv" - - "func.xyz/etc" -) - -var ( - PortUpdater = 27201 - PortCron = 27202 - PortGate = 27203 - - CardCity = struct { - UpdaterURLs []string - CronURLs []string - GateURLs []string - }{ - UpdaterURLs: []string{ - "https://updater01.cardcity.func.xyz:" + strconv.Itoa(PortUpdater), - "https://updater02.cardcity.func.xyz:" + strconv.Itoa(PortUpdater), - }, - CronURLs: []string{ - "https://cron01.cardcity.func.xyz:" + strconv.Itoa(PortCron), - "https://cron02.cardcity.func.xyz:" + strconv.Itoa(PortCron), - }, - GateURLs: []string{ - "https://gate01.cardcity.func.xyz:" + strconv.Itoa(PortGate), - "https://gate02.cardcity.func.xyz:" + strconv.Itoa(PortGate), - }, - } -) - -func init() { - etc.Register("cardcity", &CardCity) - etc.DefinePort("cardcity-updater", &PortUpdater) - etc.DefinePort("cardcity-cron", &PortCron) - etc.DefinePort("cardcity-gate", &PortGate) -} diff --git a/src/func.xyz/cardcity/internal/api/castle/handler.go b/src/func.xyz/cardcity/internal/api/castle/handler.go new file mode 100644 index 0000000..5c690e7 --- /dev/null +++ b/src/func.xyz/cardcity/internal/api/castle/handler.go @@ -0,0 +1,37 @@ +package api + +import ( + "net/http" +) + +type statefulHandler func(*serviceStates, http.ResponseWriter, *http.Request) + +var ( + handlers = map[string]statefulHandler{} +) + +type handler struct { + s *serviceStates + f statefulHandler +} + +func (h handler) ServeHTTP(resp http.ResponseWriter, req *http.Request) { + h.f(h.s, resp, req) +} + +func New() *http.ServeMux { + stat := newServiceStates() + mux := http.NewServeMux() + for pattern, sHandler := range handlers { + mux.Handle(pattern, handler{stat, sHandler}) + } + return mux +} + +func (_ *serviceStates) handlePing(resp http.ResponseWriter, _ *http.Request) { + // TODO +} + +func init() { + handlers["GET /ping"] = (*serviceStates).handlePing +} diff --git a/src/func.xyz/cardcity/internal/api/castle/service_states.go b/src/func.xyz/cardcity/internal/api/castle/service_states.go new file mode 100644 index 0000000..ab14b7a --- /dev/null +++ b/src/func.xyz/cardcity/internal/api/castle/service_states.go @@ -0,0 +1,8 @@ +package api + +type serviceStates struct { +} + +func newServiceStates() *serviceStates { + return &serviceStates{} +} diff --git a/src/func.xyz/cardcity/internal/api/gateway/handler.go b/src/func.xyz/cardcity/internal/api/gateway/handler.go new file mode 100644 index 0000000..5c690e7 --- /dev/null +++ b/src/func.xyz/cardcity/internal/api/gateway/handler.go @@ -0,0 +1,37 @@ +package api + +import ( + "net/http" +) + +type statefulHandler func(*serviceStates, http.ResponseWriter, *http.Request) + +var ( + handlers = map[string]statefulHandler{} +) + +type handler struct { + s *serviceStates + f statefulHandler +} + +func (h handler) ServeHTTP(resp http.ResponseWriter, req *http.Request) { + h.f(h.s, resp, req) +} + +func New() *http.ServeMux { + stat := newServiceStates() + mux := http.NewServeMux() + for pattern, sHandler := range handlers { + mux.Handle(pattern, handler{stat, sHandler}) + } + return mux +} + +func (_ *serviceStates) handlePing(resp http.ResponseWriter, _ *http.Request) { + // TODO +} + +func init() { + handlers["GET /ping"] = (*serviceStates).handlePing +} diff --git a/src/func.xyz/cardcity/internal/api/gateway/service_states.go b/src/func.xyz/cardcity/internal/api/gateway/service_states.go new file mode 100644 index 0000000..ab14b7a --- /dev/null +++ b/src/func.xyz/cardcity/internal/api/gateway/service_states.go @@ -0,0 +1,8 @@ +package api + +type serviceStates struct { +} + +func newServiceStates() *serviceStates { + return &serviceStates{} +} diff --git a/src/func.xyz/cardcity/internal/coheartbeat/heartbeat.go b/src/func.xyz/cardcity/internal/coheartbeat/heartbeat.go new file mode 100644 index 0000000..a37e63e --- /dev/null +++ b/src/func.xyz/cardcity/internal/coheartbeat/heartbeat.go @@ -0,0 +1,9 @@ +package heartbeat + +import ( + "func.xyz/cardcity/internal/dao" +) + +type Heartbeat struct { + record *dao.Heartbeat +} diff --git a/src/func.xyz/cardcity/internal/dao/city.go b/src/func.xyz/cardcity/internal/dao/castle.go similarity index 100% rename from src/func.xyz/cardcity/internal/dao/city.go rename to src/func.xyz/cardcity/internal/dao/castle.go diff --git a/src/func.xyz/cardcity/internal/dao/db.go b/src/func.xyz/cardcity/internal/dao/db.go index 2ac42ec..023ecb2 100644 --- a/src/func.xyz/cardcity/internal/dao/db.go +++ b/src/func.xyz/cardcity/internal/dao/db.go @@ -10,7 +10,7 @@ import ( ) const ( - dbName = "func.xyz/cardcity" + dbName = "func.xyz/cardscastle" ) type Database struct { diff --git a/src/func.xyz/cardcity/internal/dao/world_info.go b/src/func.xyz/cardcity/internal/dao/world_info.go new file mode 100644 index 0000000..56163a9 --- /dev/null +++ b/src/func.xyz/cardcity/internal/dao/world_info.go @@ -0,0 +1,40 @@ +// Copyright (c) 2024 Jack J. Mao All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package dao + +type Heartbeat struct { + Count int + Stages []struct { + Workers int + Done int + } + + db *Database +} + +func (db *Database) GetHeartbeat() (*Heartbeat, error) { + // TODO + return nil, nil +} + +func (hb *Heartbeat) CurrentStage() int { + // TODO + return -1 +} + +func (hb *Heartbeat) Refresh() error { + // TODO + return nil +} + +func (hb *Heartbeat) IncrStageDone() error { + // TODO + return nil +} + +func (hb *Heartbeat) IncrCount() error { + // TODO + return nil +} diff --git a/src/func.xyz/cardcity/internal/worker/castle_updater.go b/src/func.xyz/cardcity/internal/worker/castle_updater.go new file mode 100644 index 0000000..4df0094 --- /dev/null +++ b/src/func.xyz/cardcity/internal/worker/castle_updater.go @@ -0,0 +1 @@ +package worker diff --git a/src/func.xyz/cardcity/internal/worker/collision_resolver.go b/src/func.xyz/cardcity/internal/worker/collision_resolver.go new file mode 100644 index 0000000..4df0094 --- /dev/null +++ b/src/func.xyz/cardcity/internal/worker/collision_resolver.go @@ -0,0 +1 @@ +package worker diff --git a/src/func.xyz/cardcity/internal/worker/worker.go b/src/func.xyz/cardcity/internal/worker/worker.go new file mode 100644 index 0000000..4df0094 --- /dev/null +++ b/src/func.xyz/cardcity/internal/worker/worker.go @@ -0,0 +1 @@ +package worker diff --git a/src/func.xyz/cardcity/internal/worker/world_updater.go b/src/func.xyz/cardcity/internal/worker/world_updater.go new file mode 100644 index 0000000..4df0094 --- /dev/null +++ b/src/func.xyz/cardcity/internal/worker/world_updater.go @@ -0,0 +1 @@ +package worker diff --git a/src/func.xyz/cardcity/web/index.html b/src/func.xyz/cardcity/web/index.html deleted file mode 100644 index 5b645c8..0000000 --- a/src/func.xyz/cardcity/web/index.html +++ /dev/null @@ -1,6 +0,0 @@ - - -- 2.11.4.GIT