refactor foundry lib structure; add transport classes for db,http,websocket; add action interface instead of multiple function that accepts action classes; add changing foundry status on websocket responses

This commit is contained in:
lbenedar
2026-04-09 18:29:41 +03:00
parent 468e027418
commit fb4e371348
28 changed files with 900 additions and 791 deletions

View File

@@ -0,0 +1,40 @@
package types
type DirectionCode int
type TransportCode int
const (
WriterCode = DirectionCode(0)
ReaderCode = DirectionCode(1)
DbCode = TransportCode(0)
HttpCode = TransportCode(1)
WebSocketCode = TransportCode(2)
)
const (
RespSessionDataCode = "0"
RespPingCode = "2"
RespSessionIdCode = "40"
RespServerChangeCode = "42"
RespDataCode = "43"
ReqPongCode = "3"
ReqCreateSessionCode = "40"
ReqDataCode = "42"
)
var RequestCodes = []string{
RespSessionDataCode,
RespPingCode,
RespSessionIdCode,
RespServerChangeCode,
RespDataCode,
}
var CodesRespToReq = map[string]string{
RespSessionDataCode: ReqCreateSessionCode,
RespPingCode: ReqPongCode,
RespServerChangeCode: ReqDataCode,
//ReqDataCode: RespServerChangeCode,
}

View File

@@ -0,0 +1,24 @@
package types
import "fmt"
type FoundryError struct {
Err error
Direction DirectionCode
Type TransportCode
IsFatal bool
}
func (e *FoundryError) Error() string {
return fmt.Sprintf("Received from %d: %s", e.Type, e.Err.Error())
}
func (e *FoundryError) Unwrap() error { return e.Err }
// func NewWriterError(err error, isFatal bool) *FoundryError {
// return &FoundryError{Direction: WriterCode, Err: err, IsFatal: isFatal}
// }
// func NewReaderError(err error, isFatal bool) *FoundryError {
// return &FoundryError{Type: ReaderCode, Err: err, IsFatal: isFatal}
// }

View File

@@ -1,9 +0,0 @@
package types
import "sync"
type ExchangeChannels struct {
Msgs map[int](chan []byte)
ProgressMsg map[string](chan struct{})
ProgressMutex sync.Mutex
}

View File

@@ -0,0 +1,35 @@
package types
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/json"
type FoundryStatus struct {
IsActive bool
Version string
World string
System string
SystemVersion string
Users int
Uptime int64
}
func NewFoundryStatus(status *json.Status) *FoundryStatus {
return &FoundryStatus{
IsActive: status.Active,
Version: status.Version,
World: status.World,
System: status.System,
SystemVersion: status.SystemVersion,
Users: status.Users,
Uptime: status.Uptime,
}
}
func (fs *FoundryStatus) Update(status *json.Status) {
fs.IsActive = status.Active
fs.Version = status.Version
fs.World = status.World
fs.System = status.System
fs.SystemVersion = status.SystemVersion
fs.Users = status.Users
fs.Uptime = status.Uptime
}

View File

@@ -1,88 +0,0 @@
package types
import (
"errors"
"fmt"
"reflect"
"strings"
)
func findByTag(obj any, tag string) (int, error) {
structType := reflect.TypeOf(obj).Elem()
for i := 0; i < structType.NumField(); i++ {
fieldTags := structType.Field(i).Tag.Get("json")
if tag == strings.Split(fieldTags, ",")[0] {
return i, nil
}
}
return -1, fmt.Errorf("No such field: %s in obj", tag)
}
func setField(obj any, name string, value any) error {
structValue := reflect.ValueOf(obj).Elem()
idx, err := findByTag(obj, name)
if err != nil {
return err
}
structFieldValue := structValue.Field(idx)
if !structFieldValue.IsValid() {
return fmt.Errorf("No such field: %s in obj", name)
}
if !structFieldValue.CanSet() {
return fmt.Errorf("Cannot set %s field value", name)
}
structFieldType := structFieldValue.Type()
val := reflect.ValueOf(value)
if structFieldType != val.Type() {
return errors.New("Provided value type didn't match obj field type")
}
if value == nil {
structFieldValue.Set(reflect.Zero(structFieldType))
} else {
structFieldValue.Set(val)
}
return nil
}
func isPointer(obj any) bool {
iv := reflect.ValueOf(obj)
if !iv.IsValid() {
return false
}
return iv.Kind() == reflect.Pointer
}
func FillStruct(m map[string]any, obj any) error {
if !isPointer(obj) {
return errors.New("Passed obj is not pointer")
}
for k, v := range m {
if v == nil {
continue
}
err := setField(obj, k, v)
if err != nil {
return err
}
}
return nil
}
func Dereference(input any) any {
v := reflect.ValueOf(input)
// Check if the value is a pointer
if v.Kind() == reflect.Pointer {
// v.Elem() returns the value the pointer points to
return v.Elem().Interface()
}
return input
}

View File

@@ -1,72 +0,0 @@
package types
import "fmt"
const (
WsSessionType = "session"
WsProgressType = "progress"
WsUserActivityType = "userActivity"
WsShutdownType = "shutdown"
)
var WsMsgActions = map[string]func() any{
"session": func() any { return &WsSessionMsg{} },
"progress": func() any { return &WsProgressMsg{} },
// "userActivity": WsUserActivityCursor{},
"shutdown": func() any { return &WsShutdownMsg{} },
}
func GetWsMsgAction(dataType string) any {
if factory, ok := WsMsgActions[dataType]; ok {
return factory()
}
return nil
}
type WsSessionMsg struct {
SessionId string `json:"sessionId"`
UserId *string `json:"userId,omitempty"`
}
func (msg WsSessionMsg) Action() error {
fmt.Println("WsSessionMsg")
return nil
}
type WsProgressMsg struct {
Id string `json:"id"`
Message string `json:"message"`
Pct float64 `json:"pct"`
HasChanged bool `json:"hasChanged,omitempty"`
Act string `json:"action"`
Step string `json:"step"`
}
func (msg WsProgressMsg) Action() error {
fmt.Println("WsProgressMsg")
return nil
}
type WsUserActivityMsg struct {
Cursor WsUserActivityCursor `json:"cursor"`
}
type WsUserActivityCursor struct {
X int `json:"x"`
Y int `json:"y"`
}
func (msg WsUserActivityMsg) Action() error {
fmt.Println("WsUserActivityMsg")
return nil
}
type WsShutdownMsg struct {
World string `json:"world"`
UserId *string `json:"userId,omitempty"`
}
func (msg WsShutdownMsg) Action() error {
fmt.Println("WsShutdownMsg")
return nil
}

View File

@@ -1,5 +1,13 @@
package types
import "sync"
type ExchangeChannels struct {
Msgs map[int](chan []byte)
ProgressMsg map[string](chan struct{})
ProgressMutex sync.Mutex
}
type ReadChannels struct {
done chan struct{}
msg chan *WsMessage
@@ -25,11 +33,9 @@ func (channels *ReadChannels) Close() {
}
func InitWsChannels() *ReadChannels {
channels := ReadChannels{
return &ReadChannels{
done: make(chan struct{}),
err: make(chan error, 10),
msg: make(chan *WsMessage, 10),
}
return &channels
}

View File

@@ -1,6 +1,17 @@
package types
import "fmt"
import (
"errors"
"fmt"
"strconv"
"strings"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/requests"
)
var (
ErrorMsgNotHaveNumber = errors.New("Message doesn't have dataCode and id")
)
type WsMessage struct {
Code string
@@ -15,3 +26,58 @@ func (w WsMessage) ToString() string {
func (w WsMessage) ToByteSlice() []byte {
return fmt.Appendf([]byte{}, "%s%d%s", w.Code, w.Id, w.MsgJson)
}
func parseCode(msg *string, requestCodes []string) string {
for j := range requestCodes {
if !strings.HasPrefix(*msg, requestCodes[j]) {
continue
}
return requestCodes[j]
}
return ""
}
func parseId(msg *string, start int) (int, int) {
msgLen := len(*msg)
j := start
for ; j < msgLen; j++ {
if (*msg)[j] < '0' || (*msg)[j] > '9' {
break
}
}
if j >= msgLen || j <= 0 {
return 0, 0
}
msgId, err := strconv.Atoi((*msg)[start:j])
if err != nil {
return 0, 0
}
return msgId, j
}
func ParseWsRespMessage(msg string, requestCodes []string) (*WsMessage, error) {
data := &WsMessage{}
data.Code = parseCode(&msg, requestCodes)
i := len(data.Code)
if i == 0 {
return nil, ErrorMsgNotHaveNumber
}
data.Id, i = parseId(&msg, i)
if i == 0 {
i = len(data.Code)
}
// if i == 0 {
// return nil, ErrorMsgNotHaveNumber
// }
data.MsgJson = msg[i:]
return data, nil
}
func NewWsMessageByPage(page string, currWsId int) *WsMessage {
return &WsMessage{Code: CodesRespToReq[RespServerChangeCode], Id: currWsId, MsgJson: fmt.Sprintf("[\"%s\"]", requests.WsTypeData[page])}
}