From b04843d758a07803658f1c0220a2626072857dee Mon Sep 17 00:00:00 2001 From: Volte6 <143822+Volte6@users.noreply.github.com> Date: Sat, 21 Jun 2025 19:48:16 -0700 Subject: [PATCH 1/9] initial migration code --- Makefile | 3 +- _datafiles/config.yaml | 1 + .../tutorial/scripts/58-training_dummy.js | 9 +- .../world/default/rooms/catacombs/32.yaml | 15 ++- .../tutorial/scripts/58-training_dummy.js | 8 +- internal/configs/config.server.go | 5 + internal/migration/1.0.0.go | 70 ++++++++++++++ internal/migration/migration.go | 23 +++++ internal/version/version.go | 94 +++++++++++++++++++ internal/version/version_test.go | 81 ++++++++++++++++ main.go | 21 +++++ 11 files changed, 313 insertions(+), 17 deletions(-) create mode 100644 internal/migration/1.0.0.go create mode 100644 internal/migration/migration.go create mode 100644 internal/version/version.go create mode 100644 internal/version/version_test.go diff --git a/Makefile b/Makefile index b7939b27..4dfdf8c6 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ + .DEFAULT_GOAL := build VERSION ?= $(shell git rev-parse HEAD) @@ -120,7 +121,7 @@ coverage: rm -rf bin .PHONY: js-lint -js-lint: +js-lint: ### Run Javascript linter # Grep filtering it to remove errors reported by docker image around npm packages # if "### errors" is found in the output, exits with an error code of 1 # This should allow us to use it in CI/CD diff --git a/_datafiles/config.yaml b/_datafiles/config.yaml index c6780fc3..d42ea917 100755 --- a/_datafiles/config.yaml +++ b/_datafiles/config.yaml @@ -78,6 +78,7 @@ Server: # accidental changes that could break the game. Locked: - FilePaths + - Server.CurrentVersion - Server.NextRoomId - Server.Seed - Server.OnLoginCommands diff --git a/_datafiles/world/default/mobs/tutorial/scripts/58-training_dummy.js b/_datafiles/world/default/mobs/tutorial/scripts/58-training_dummy.js index cbf4b9c6..536c3624 100644 --- a/_datafiles/world/default/mobs/tutorial/scripts/58-training_dummy.js +++ b/_datafiles/world/default/mobs/tutorial/scripts/58-training_dummy.js @@ -4,8 +4,9 @@ function onDie(mob, room, eventDetails) { room.SendText( mob.GetCharacterName(true) + " crumbles to dust." ); - room.GetMob(teacherMobId, true); - - teacherMob.Command('say You did it! As you can see you gain experience points for combat victories.'); - teacherMob.Command('say Now head west to complete your training.', 2.0); + var teacherMob = room.GetMob(teacherMobId, true); + if ( teacherMob != null ) { + teacherMob.Command('say You did it! As you can see you gain experience points for combat victories.'); + teacherMob.Command('say Now head west to complete your training.', 2.0); + } } diff --git a/_datafiles/world/default/rooms/catacombs/32.yaml b/_datafiles/world/default/rooms/catacombs/32.yaml index 80678b08..0c2fe72f 100755 --- a/_datafiles/world/default/rooms/catacombs/32.yaml +++ b/_datafiles/world/default/rooms/catacombs/32.yaml @@ -1,15 +1,12 @@ roomid: 32 zone: Catacombs title: Entrance to the Catacombs -description: Beneath the Sanctuary of the Benevolent Heart lies the labyrinthine expanse - known as the Catacombs. A network of narrow, winding tunnels and chambers, the Catacombs - are carved from the cold, unyielding stone of Frostfang's bedrock. The air within - is thick with age and whispers of the past, carrying a palpable weight of reverence - and mystery. Walls are lined with alcoves, each holding the remains of the departed, - their final resting places marked with inscriptions and symbols of their beliefs. - Faintly glowing lanterns, placed at irregular intervals, cast eerie illuminations - on the ancient bones and artifacts, while the distant echo of dripping water serves - as a haunting reminder of the passage of time in this sacred underworld. +zoneconfig: + roomid: 32 + autoscale: + minimum: 8 + maximum: 13 + musicfile: static/audio/music/catacombs.mp3 mapsymbol: E maplegend: Entrance exits: diff --git a/_datafiles/world/empty/mobs/tutorial/scripts/58-training_dummy.js b/_datafiles/world/empty/mobs/tutorial/scripts/58-training_dummy.js index 5b3e2c9b..536c3624 100644 --- a/_datafiles/world/empty/mobs/tutorial/scripts/58-training_dummy.js +++ b/_datafiles/world/empty/mobs/tutorial/scripts/58-training_dummy.js @@ -4,7 +4,9 @@ function onDie(mob, room, eventDetails) { room.SendText( mob.GetCharacterName(true) + " crumbles to dust." ); - teacherMob = room.GetMob(teacherMobId, true); - - teacherMob.Command('say You did it! Head west to complete your training.'); + var teacherMob = room.GetMob(teacherMobId, true); + if ( teacherMob != null ) { + teacherMob.Command('say You did it! As you can see you gain experience points for combat victories.'); + teacherMob.Command('say Now head west to complete your training.', 2.0); + } } diff --git a/internal/configs/config.server.go b/internal/configs/config.server.go index 5314e47f..723dc1ef 100644 --- a/internal/configs/config.server.go +++ b/internal/configs/config.server.go @@ -2,6 +2,7 @@ package configs type Server struct { MudName ConfigString `yaml:"MudName"` // Name of the MUD + CurrentVersion ConfigString `yaml:"CurrentVersion"` // Current version this mud has been updated to Seed ConfigSecret `yaml:"Seed"` // Seed that may be used for generating content MaxCPUCores ConfigInt `yaml:"MaxCPUCores"` // How many cores to allow for multi-core operations OnLoginCommands ConfigSliceString `yaml:"OnLoginCommands"` // Commands to run when a user logs in @@ -26,6 +27,10 @@ func (s *Server) Validate() { s.MaxCPUCores = 0 // default } + if s.CurrentVersion == `` { + s.CurrentVersion = `0.9.0` // If no version found, failover to a known version + } + } func GetServerConfig() Server { diff --git a/internal/migration/1.0.0.go b/internal/migration/1.0.0.go new file mode 100644 index 00000000..b2dc014c --- /dev/null +++ b/internal/migration/1.0.0.go @@ -0,0 +1,70 @@ +package migration + +import ( + "fmt" + "os" + "path/filepath" + "regexp" + + "github.com/GoMudEngine/GoMud/internal/configs" + "github.com/GoMudEngine/GoMud/internal/mudlog" + "gopkg.in/yaml.v2" +) + +// Description: +// rooms.Room.ZoneConfig was removed when Zone data was migrated to zone-config.yaml in zone folders +// This function loads all of the yaml files in the DATAFILES/world/*/rooms/* and looks for any ZoneConfig data. +// If found, the data is moved to a zone-config.yaml file, and the ZoneConfig data in the Room datafile is removed. +func migrate_RoomZoneConfig() error { + + c := configs.GetConfig() + + worldfilesGlob := filepath.Join(string(c.FilePaths.DataFiles), "rooms", "*", "*.yaml") + matches, err := filepath.Glob(worldfilesGlob) + + if err != nil { + return err + } + + // We only care about room files, so ###.yaml (possible negative) + re := regexp.MustCompile(`^[\-0-9]+\.yaml$`) + for _, path := range matches { + + filename := filepath.Base(path) + if !re.MatchString(filename) { + continue + } + + data, err := os.ReadFile(path) + if err != nil { + return err + } + + filedata := map[string]any{} + + err = yaml.Unmarshal(data, &filedata) + if err != nil { + return fmt.Errorf("failed to parse YAML: %w", err) + } + + if filedata[`zoneconfig`] == nil { + continue + } + + mudlog.Info("ZoneConfig found", "path", path) + //fmt.Println(filedata[`zoneconfig`]) + + filedata[`zoneconfig`] = nil + + fdata, _ := yaml.Marshal(filedata) + + info, _ := os.Stat(path) + + if err := os.WriteFile(path, fdata, info.Mode().Perm()); err != nil { + return err + } + + } + + return nil +} diff --git a/internal/migration/migration.go b/internal/migration/migration.go new file mode 100644 index 00000000..b59c8c4c --- /dev/null +++ b/internal/migration/migration.go @@ -0,0 +1,23 @@ +package migration + +import ( + "github.com/GoMudEngine/GoMud/internal/version" +) + +func Run(binVersion version.Version) error { + + // + // Note: Follow this pattern and keep these version upgrades in order of lowest to greatest to avoid problems + // + + // 0.0.0 -> 1.0.0 + if binVersion.IsOlderThan(version.New(1, 0, 0)) { + + if err := migrate_RoomZoneConfig(); err != nil { + return err + } + + } + + return nil +} diff --git a/internal/version/version.go b/internal/version/version.go new file mode 100644 index 00000000..ce2ad515 --- /dev/null +++ b/internal/version/version.go @@ -0,0 +1,94 @@ +package version + +import ( + "fmt" + "strconv" + "strings" +) + +const ( + Older = -1 + Newer = 1 + Equal = 0 +) + +type Version struct { + Major int + Minor int + Patch int +} + +func (v Version) String() string { + return fmt.Sprintf(`%d.%d.%d`, v.Major, v.Minor, v.Patch) +} + +func (v Version) Compare(other Version) int { + if v.Major != other.Major { + if v.Major < other.Major { + return Older + } + return Newer + } + if v.Minor != other.Minor { + if v.Minor < other.Minor { + return Older + } + return Newer + } + if v.Patch != other.Patch { + if v.Patch < other.Patch { + return Older + } + return Newer + } + return Equal +} + +func (v Version) IsNewerThan(other Version) bool { + return v.Compare(other) >= Newer +} + +func (v Version) IsOlderThan(other Version) bool { + return v.Compare(other) <= Older +} + +func New(major int, minor int, patch int) Version { + return Version{major, minor, patch} +} + +func Parse(v string) (Version, error) { + // lowercase it all for predicatability + s := strings.ToLower(v) + + // Remove leading "v" if present + s = strings.TrimPrefix(s, "v") + + parts := strings.Split(s, ".") + if len(parts) < 2 || len(parts) > 3 { + return Version{}, fmt.Errorf("invalid version format: %s", s) + } + + major, err := strconv.Atoi(parts[0]) + if err != nil { + return Version{}, fmt.Errorf("invalid major version: %v", err) + } + + minor, err := strconv.Atoi(parts[1]) + if err != nil { + return Version{}, fmt.Errorf("invalid minor version: %v", err) + } + + patch := 0 + if len(parts) == 3 { + patch, err = strconv.Atoi(parts[2]) + if err != nil { + return Version{}, fmt.Errorf("invalid patch version: %v", err) + } + } + + if major == 0 && minor == 0 && patch == 0 { + return Version{}, fmt.Errorf("invalid version: %s", v) + } + + return Version{Major: major, Minor: minor, Patch: patch}, nil +} diff --git a/internal/version/version_test.go b/internal/version/version_test.go new file mode 100644 index 00000000..3077a3f9 --- /dev/null +++ b/internal/version/version_test.go @@ -0,0 +1,81 @@ +package version + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParse(t *testing.T) { + tests := []struct { + input string + expected Version + hasError bool + }{ + {"0.9.0", Version{0, 9, 0}, false}, + {"v0.9.0", Version{0, 9, 0}, false}, + {"1.2.3", Version{1, 2, 3}, false}, + {"v1.2.3", Version{1, 2, 3}, false}, + {"2.0", Version{2, 0, 0}, false}, + {"v2.0", Version{2, 0, 0}, false}, + {"10.20.30", Version{10, 20, 30}, false}, + {"V10.20.30", Version{10, 20, 30}, false}, + + // Invalid cases + {"", Version{}, true}, + {"v", Version{}, true}, + {"1", Version{}, true}, + {"v1", Version{}, true}, + {"0.0.0", Version{0, 0, 0}, true}, + {"v0.0.0", Version{0, 0, 0}, true}, + {"1.2.3.4", Version{}, true}, + {"v1.2.beta", Version{}, true}, + {"abc", Version{}, true}, + } + + for _, tt := range tests { + v, err := Parse(tt.input) + if tt.hasError { + assert.Error(t, err, "expected error for input: %q", tt.input) + } else { + assert.NoError(t, err, "unexpected error for input: %q", tt.input) + assert.Equal(t, tt.expected, v, "parsed version mismatch for input: %q", tt.input) + } + } +} + +func TestVersionCompare(t *testing.T) { + tests := []struct { + v1 Version + v2 Version + expected int // -1 = v1 older, 0 = equal, 1 = v1 newer + }{ + {Version{1, 0, 0}, Version{1, 0, 0}, 0}, + {Version{1, 2, 3}, Version{1, 2, 3}, 0}, + {Version{2, 0, 0}, Version{1, 9, 9}, 1}, + {Version{1, 10, 0}, Version{1, 9, 9}, 1}, + {Version{1, 2, 5}, Version{1, 2, 3}, 1}, + {Version{1, 0, 0}, Version{2, 0, 0}, -1}, + {Version{1, 2, 0}, Version{1, 3, 0}, -1}, + {Version{1, 2, 3}, Version{1, 2, 4}, -1}, + } + + for _, tt := range tests { + result := tt.v1.Compare(tt.v2) + assert.Equal(t, tt.expected, result, "Compare(%+v, %+v)", tt.v1, tt.v2) + } +} + +func TestVersionIsNewerThan(t *testing.T) { + assert.True(t, Version{2, 0, 0}.IsNewerThan(Version{1, 9, 9})) + assert.True(t, Version{1, 2, 3}.IsNewerThan(Version{1, 2, 2})) + assert.False(t, Version{1, 2, 3}.IsNewerThan(Version{1, 2, 3})) + assert.False(t, Version{1, 0, 0}.IsNewerThan(Version{1, 1, 0})) +} + +func TestVersionIsOlderThan(t *testing.T) { + assert.True(t, Version{1, 0, 0}.IsOlderThan(Version{1, 1, 0})) + assert.True(t, Version{1, 2, 2}.IsOlderThan(Version{1, 2, 3})) + assert.False(t, Version{1, 2, 3}.IsOlderThan(Version{1, 2, 3})) + assert.False(t, Version{2, 0, 0}.IsOlderThan(Version{1, 9, 9})) +} diff --git a/main.go b/main.go index 538f3612..0284afae 100644 --- a/main.go +++ b/main.go @@ -32,7 +32,9 @@ import ( "github.com/GoMudEngine/GoMud/internal/items" "github.com/GoMudEngine/GoMud/internal/keywords" "github.com/GoMudEngine/GoMud/internal/language" + "github.com/GoMudEngine/GoMud/internal/migration" "github.com/GoMudEngine/GoMud/internal/usercommands" + "github.com/GoMudEngine/GoMud/internal/version" "github.com/gorilla/websocket" "github.com/GoMudEngine/GoMud/internal/mapper" @@ -56,6 +58,13 @@ import ( textLang "golang.org/x/text/language" ) +// Version of the binary +// Should be kept in lockstep with github releases +// When updating this version: +// 1. Expect to update the github release version +// 2. Consider whether any migration code is needed for breaking changes, particularly in datafiles (see internal/migration) +const VERSION = "1.0.0" + var ( sigChan = make(chan os.Signal, 1) workerShutdownChan = make(chan bool, 1) @@ -96,6 +105,18 @@ func main() { configs.ReloadConfig() c := configs.GetConfig() + lastKnownVersion, err := version.Parse(string(configs.GetServerConfig().CurrentVersion)) + if err != nil { + mudlog.Error("Versioning", "error", err) + os.Exit(1) + } + + if err = migration.Run(lastKnownVersion); err != nil { + mudlog.Error("migration.Run()", "error", err) + os.Exit(1) + } + + return // Default i18n localize folders if len(c.Translation.LanguagePaths) == 0 { c.Translation.LanguagePaths = []string{ From 4f1fe15ddcdfab0e85cbc689a79ae5fe718a72da Mon Sep 17 00:00:00 2001 From: Volte6 <143822+Volte6@users.noreply.github.com> Date: Sat, 21 Jun 2025 19:51:54 -0700 Subject: [PATCH 2/9] adjustments --- _datafiles/world/default/rooms/catacombs/32.yaml | 11 ++++++++++- internal/migration/1.0.0.go | 8 ++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/_datafiles/world/default/rooms/catacombs/32.yaml b/_datafiles/world/default/rooms/catacombs/32.yaml index 0c2fe72f..d50eae05 100755 --- a/_datafiles/world/default/rooms/catacombs/32.yaml +++ b/_datafiles/world/default/rooms/catacombs/32.yaml @@ -7,6 +7,15 @@ zoneconfig: minimum: 8 maximum: 13 musicfile: static/audio/music/catacombs.mp3 +description: Beneath the Sanctuary of the Benevolent Heart lies the labyrinthine expanse + known as the Catacombs. A network of narrow, winding tunnels and chambers, the Catacombs + are carved from the cold, unyielding stone of Frostfang's bedrock. The air within + is thick with age and whispers of the past, carrying a palpable weight of reverence + and mystery. Walls are lined with alcoves, each holding the remains of the departed, + their final resting places marked with inscriptions and symbols of their beliefs. + Faintly glowing lanterns, placed at irregular intervals, cast eerie illuminations + on the ancient bones and artifacts, while the distant echo of dripping water serves + as a haunting reminder of the passage of time in this sacred underworld. mapsymbol: E maplegend: Entrance exits: @@ -21,4 +30,4 @@ exits: spawninfo: - mobid: 21 message: A dark robed figure slinks into view. - respawnrate: 10 real minutes + respawnrate: 10 real minutes \ No newline at end of file diff --git a/internal/migration/1.0.0.go b/internal/migration/1.0.0.go index b2dc014c..538e86fe 100644 --- a/internal/migration/1.0.0.go +++ b/internal/migration/1.0.0.go @@ -7,7 +7,9 @@ import ( "regexp" "github.com/GoMudEngine/GoMud/internal/configs" + "github.com/GoMudEngine/GoMud/internal/fileloader" "github.com/GoMudEngine/GoMud/internal/mudlog" + "github.com/GoMudEngine/GoMud/internal/rooms" "gopkg.in/yaml.v2" ) @@ -64,6 +66,12 @@ func migrate_RoomZoneConfig() error { return err } + roomPtr, err := fileloader.LoadFlatFile[*rooms.Room](path) + if err != nil { + return err + } + + rooms.SaveRoomTemplate(*roomPtr) } return nil From f931dce3f74e05ef31c2e023e229be897ea9ae2d Mon Sep 17 00:00:00 2001 From: Volte6 <143822+Volte6@users.noreply.github.com> Date: Sat, 21 Jun 2025 21:05:30 -0700 Subject: [PATCH 3/9] Finalizing migraiton code --- .../world/default/rooms/catacombs/32.yaml | 8 +- internal/migration/1.0.0.go | 92 +++++++++++++++++-- internal/migration/migration.go | 10 +- internal/rooms/rooms.go | 56 ++++++----- internal/rooms/save_and_load.go | 22 ----- main.go | 24 +++-- 6 files changed, 136 insertions(+), 76 deletions(-) diff --git a/_datafiles/world/default/rooms/catacombs/32.yaml b/_datafiles/world/default/rooms/catacombs/32.yaml index d50eae05..80678b08 100755 --- a/_datafiles/world/default/rooms/catacombs/32.yaml +++ b/_datafiles/world/default/rooms/catacombs/32.yaml @@ -1,12 +1,6 @@ roomid: 32 zone: Catacombs title: Entrance to the Catacombs -zoneconfig: - roomid: 32 - autoscale: - minimum: 8 - maximum: 13 - musicfile: static/audio/music/catacombs.mp3 description: Beneath the Sanctuary of the Benevolent Heart lies the labyrinthine expanse known as the Catacombs. A network of narrow, winding tunnels and chambers, the Catacombs are carved from the cold, unyielding stone of Frostfang's bedrock. The air within @@ -30,4 +24,4 @@ exits: spawninfo: - mobid: 21 message: A dark robed figure slinks into view. - respawnrate: 10 real minutes \ No newline at end of file + respawnrate: 10 real minutes diff --git a/internal/migration/1.0.0.go b/internal/migration/1.0.0.go index 538e86fe..7b55ea15 100644 --- a/internal/migration/1.0.0.go +++ b/internal/migration/1.0.0.go @@ -7,7 +7,6 @@ import ( "regexp" "github.com/GoMudEngine/GoMud/internal/configs" - "github.com/GoMudEngine/GoMud/internal/fileloader" "github.com/GoMudEngine/GoMud/internal/mudlog" "github.com/GoMudEngine/GoMud/internal/rooms" "gopkg.in/yaml.v2" @@ -19,6 +18,27 @@ import ( // If found, the data is moved to a zone-config.yaml file, and the ZoneConfig data in the Room datafile is removed. func migrate_RoomZoneConfig() error { + // This struct is how ZoneConfig looked as of 1.0.0 + // Since we will be upgrading an older version to this format, use a copy of the struct from that period + // To ensure we aren't using a struct that has changed over time + type zoneConfig_1_0_0 struct { + Name string `yaml:"name,omitempty"` + RoomId int `yaml:"roomid,omitempty"` + MobAutoScale struct { + Minimum int `yaml:"minimum,omitempty"` // level scaling minimum + Maximum int `yaml:"maximum,omitempty"` // level scaling maximum + } `yaml:"autoscale,omitempty"` // level scaling range if any + Mutators []struct { + MutatorId string `yaml:"mutatorid,omitempty"` // Short text that will uniquely identify this modifier ("dusty") + SpawnedRound uint64 `yaml:"spawnedround,omitempty"` // Tracks when this mutator was created (useful for decay) + DespawnedRound uint64 `yaml:"despawnedround,omitempty"` // Track when it decayed to nothing. + } `yaml:"mutators,omitempty"` + IdleMessages []string `yaml:"idlemessages,omitempty"` // list of messages that can be displayed to players in the zone, assuming a room has none defined + MusicFile string `yaml:"musicfile,omitempty"` // background music to play when in this zone + DefaultBiome string `yaml:"defaultbiome,omitempty"` // city, swamp etc. see biomes.go + RoomIds map[int]struct{} `yaml:"-"` // Does not get written. Built dyanmically when rooms are loaded. + } + c := configs.GetConfig() worldfilesGlob := filepath.Join(string(c.FilePaths.DataFiles), "rooms", "*", "*.yaml") @@ -53,25 +73,81 @@ func migrate_RoomZoneConfig() error { continue } - mudlog.Info("ZoneConfig found", "path", path) - //fmt.Println(filedata[`zoneconfig`]) + mudlog.Info("Migration 1.0.0", "file", path, "message", "migrating zoneconfig from room data file to zone-config.yaml") + + // + // From here on out, this code migrates zoneconfig data out of room file and into zone-config.yaml + // + roomFileInfo, _ := os.Stat(path) + + mudlog.Info("Migration 1.0.0", "file", path, "message", "isolating zoneconfig data") + + // Isolate the zoneconfig and write it to its own zone-config.yaml file + zoneBytes, err := yaml.Marshal(filedata[`zoneconfig`]) + if err != nil { + return err + } + + zoneDataStruct := zoneConfig_1_0_0{} + + if err = yaml.Unmarshal(zoneBytes, &zoneDataStruct); err != nil { + return err + } + + if filedata[`zone`] != nil { + if zoneName, ok := filedata[`zone`].(string); ok { + zoneDataStruct.Name = zoneName + } else { + zoneDataStruct.Name = filedata[`title`].(string) + } + + if defaultBiome, ok := filedata[`biome`].(string); ok { + zoneDataStruct.DefaultBiome = defaultBiome + } + } + zoneFileBytes, err := yaml.Marshal(zoneDataStruct) + if err != nil { + return err + } + + zoneFilePath := filepath.Join(filepath.Dir(path), "zone-config.yaml") + + mudlog.Info("Migration 1.0.0", "file", path, "message", "writing "+zoneFilePath) + + if err := os.WriteFile(zoneFilePath, zoneFileBytes, roomFileInfo.Mode().Perm()); err != nil { + return err + } + + // Now clear "zoneconfig" and write the room data back filedata[`zoneconfig`] = nil - fdata, _ := yaml.Marshal(filedata) + mudlog.Info("Migration 1.0.0", "file", path, "message", "writing modified room data") - info, _ := os.Stat(path) + // First marshal the modified room data into bytes + modifiedRoomBytes, err := yaml.Marshal(filedata) + if err != nil { + return err + } - if err := os.WriteFile(path, fdata, info.Mode().Perm()); err != nil { + // Unmarshal the bytes into the proper struct + modifiedRoomStruct := rooms.Room{} + if err = yaml.Unmarshal(modifiedRoomBytes, &modifiedRoomStruct); err != nil { return err } - roomPtr, err := fileloader.LoadFlatFile[*rooms.Room](path) + // Marshal again, this time using the proper struct + modifiedRoomBytes, err = yaml.Marshal(modifiedRoomStruct) if err != nil { return err } - rooms.SaveRoomTemplate(*roomPtr) + if err := os.WriteFile(path, modifiedRoomBytes, roomFileInfo.Mode().Perm()); err != nil { + return err + } + + mudlog.Info("Migration 1.0.0", "file", path, "message", "successfully updated") + } return nil diff --git a/internal/migration/migration.go b/internal/migration/migration.go index b59c8c4c..722cec2b 100644 --- a/internal/migration/migration.go +++ b/internal/migration/migration.go @@ -1,17 +1,18 @@ package migration import ( + "github.com/GoMudEngine/GoMud/internal/configs" "github.com/GoMudEngine/GoMud/internal/version" ) -func Run(binVersion version.Version) error { +func Run(lastConfigVersion version.Version, serverVersion version.Version) error { // // Note: Follow this pattern and keep these version upgrades in order of lowest to greatest to avoid problems // // 0.0.0 -> 1.0.0 - if binVersion.IsOlderThan(version.New(1, 0, 0)) { + if lastConfigVersion.IsOlderThan(version.New(1, 0, 0)) { if err := migrate_RoomZoneConfig(); err != nil { return err @@ -19,5 +20,10 @@ func Run(binVersion version.Version) error { } + // + // Finally, update to the version this migration is for + // + configs.SetVal(`Server.CurrentVersion`, serverVersion.String()) + return nil } diff --git a/internal/rooms/rooms.go b/internal/rooms/rooms.go index 32363e5d..4cd74bfe 100644 --- a/internal/rooms/rooms.go +++ b/internal/rooms/rooms.go @@ -30,7 +30,6 @@ var ( "*": defaultMapSymbol, //"•": "*", } - ) type FindFlag uint16 @@ -65,34 +64,33 @@ const ( type Room struct { //mutex - RoomId int `yaml:"roomid"` // a unique numeric index of the room. Also the filename. - Zone string `yaml:"zone"` // zone is a way to partition rooms into groups. Also into folders. - ZoneConfig *ZoneConfig `yaml:"zoneconfig,omitempty" instance:"skip"` // If non-null is a root room. - MusicFile string `yaml:"musicfile,omitempty"` // background music to play when in this room - IsBank bool `yaml:"isbank,omitempty"` // Is this a bank room? If so, players can deposit/withdraw gold here. - IsStorage bool `yaml:"isstorage,omitempty"` // Is this a storage room? If so, players can add/remove objects here. - IsCharacterRoom bool `yaml:"ischaracterroom,omitempty"` // Is this a room where characters can create new characters to swap between them? - Title string `yaml:"title"` // Title shown to the user - Description string `yaml:"description"` // Description shown to the user - MapSymbol string `yaml:"mapsymbol,omitempty"` // The symbol to use when generating a map of the zone - MapLegend string `yaml:"maplegend,omitempty"` // The text to display in the legend for this room. Should be one word. - Biome string `yaml:"biome,omitempty"` // The biome of the room. Used for weather generation. - Containers map[string]Container `yaml:"containers,omitempty"` // If this room has a chest, what is in it? - Exits map[string]exit.RoomExit `yaml:"exits"` // Exits to other rooms - ExitsTemp map[string]exit.TemporaryRoomExit `yaml:"-"` // Temporary exits that will be removed after a certain time. Don't bother saving on sever shutting down. - Nouns map[string]string `yaml:"nouns,omitempty"` // Interesting nouns to highlight in the room or reveal on succesful searches. - Items []items.Item `yaml:"items,omitempty"` // Items on the floor - Stash []items.Item `yaml:"stash,omitempty"` // list of items in the room that are not visible to players - Corpses []Corpse `yaml:"-"` // Any corpses laying around from recent deaths - Gold int `yaml:"gold,omitempty"` // How much gold is on the ground? - SpawnInfo []SpawnInfo `yaml:"spawninfo,omitempty" instance:"skip"` // key is creature ID, value is spawn chance - SkillTraining map[string]TrainingRange `yaml:"skilltraining,omitempty"` // list of skills that can be trained in this room - Signs []Sign `yaml:"sign,omitempty"` // list of scribbles in the room - IdleMessages []string `yaml:"idlemessages,omitempty" ` // list of messages that can be displayed to players in the room - LastIdleMessage uint8 `yaml:"-"` // index of the last idle message displayed - LongTermDataStore map[string]any `yaml:"longtermdatastore,omitempty"` // Long term data store for the room - Mutators mutators.MutatorList `yaml:"mutators,omitempty"` // mutators this room spawns with. - Pvp bool `yaml:"pvp,omitempty"` // if config pvp is set to `limited`, uses this value + RoomId int `yaml:"roomid"` // a unique numeric index of the room. Also the filename. + Zone string `yaml:"zone"` // zone is a way to partition rooms into groups. Also into folders. + MusicFile string `yaml:"musicfile,omitempty"` // background music to play when in this room + IsBank bool `yaml:"isbank,omitempty"` // Is this a bank room? If so, players can deposit/withdraw gold here. + IsStorage bool `yaml:"isstorage,omitempty"` // Is this a storage room? If so, players can add/remove objects here. + IsCharacterRoom bool `yaml:"ischaracterroom,omitempty"` // Is this a room where characters can create new characters to swap between them? + Title string `yaml:"title"` // Title shown to the user + Description string `yaml:"description"` // Description shown to the user + MapSymbol string `yaml:"mapsymbol,omitempty"` // The symbol to use when generating a map of the zone + MapLegend string `yaml:"maplegend,omitempty"` // The text to display in the legend for this room. Should be one word. + Biome string `yaml:"biome,omitempty"` // The biome of the room. Used for weather generation. + Containers map[string]Container `yaml:"containers,omitempty"` // If this room has a chest, what is in it? + Exits map[string]exit.RoomExit `yaml:"exits"` // Exits to other rooms + ExitsTemp map[string]exit.TemporaryRoomExit `yaml:"-"` // Temporary exits that will be removed after a certain time. Don't bother saving on sever shutting down. + Nouns map[string]string `yaml:"nouns,omitempty"` // Interesting nouns to highlight in the room or reveal on succesful searches. + Items []items.Item `yaml:"items,omitempty"` // Items on the floor + Stash []items.Item `yaml:"stash,omitempty"` // list of items in the room that are not visible to players + Corpses []Corpse `yaml:"-"` // Any corpses laying around from recent deaths + Gold int `yaml:"gold,omitempty"` // How much gold is on the ground? + SpawnInfo []SpawnInfo `yaml:"spawninfo,omitempty" instance:"skip"` // key is creature ID, value is spawn chance + SkillTraining map[string]TrainingRange `yaml:"skilltraining,omitempty"` // list of skills that can be trained in this room + Signs []Sign `yaml:"sign,omitempty"` // list of scribbles in the room + IdleMessages []string `yaml:"idlemessages,omitempty" ` // list of messages that can be displayed to players in the room + LastIdleMessage uint8 `yaml:"-"` // index of the last idle message displayed + LongTermDataStore map[string]any `yaml:"longtermdatastore,omitempty"` // Long term data store for the room + Mutators mutators.MutatorList `yaml:"mutators,omitempty"` // mutators this room spawns with. + Pvp bool `yaml:"pvp,omitempty"` // if config pvp is set to `limited`, uses this value // Unexported/private players []int // list of user IDs currently in the room mobs []int // list of mob instance IDs currently in the room. Does not get saved. diff --git a/internal/rooms/save_and_load.go b/internal/rooms/save_and_load.go index 180c3b3d..5bdd348f 100644 --- a/internal/rooms/save_and_load.go +++ b/internal/rooms/save_and_load.go @@ -400,28 +400,6 @@ func loadAllRoomZones() error { for _, loadedRoom := range loadedRooms { - // - // This code migrates old format data to the new format (separate zone file) - // - if loadedRoom.ZoneConfig != nil { - if loadedRoom.ZoneConfig.RoomId == loadedRoom.RoomId { - if _, ok := roomManager.zones[loadedRoom.Zone]; !ok { - newZone := NewZoneConfig(loadedRoom.Zone) - newZone.DefaultBiome = loadedRoom.Biome - newZone.IdleMessages = loadedRoom.ZoneConfig.IdleMessages - newZone.MusicFile = loadedRoom.ZoneConfig.MusicFile - newZone.MobAutoScale = loadedRoom.ZoneConfig.MobAutoScale - newZone.Mutators = loadedRoom.ZoneConfig.Mutators - newZone.RoomId = loadedRoom.ZoneConfig.RoomId - if err := SaveZoneConfig(newZone); err != nil { - return err - } - loadedRoom.ZoneConfig = nil // if successfully saved, blank out the ZoneConfig for the room - SaveRoomTemplate(*loadedRoom) - } - } - } - // configs.GetConfig().DeathRecoveryRoom is the death/shadow realm and gets a pass if loadedRoom.RoomId == int(configs.GetSpecialRoomsConfig().DeathRecoveryRoom) { continue diff --git a/main.go b/main.go index 0284afae..ca9dbcd2 100644 --- a/main.go +++ b/main.go @@ -111,12 +111,13 @@ func main() { os.Exit(1) } - if err = migration.Run(lastKnownVersion); err != nil { + currentVersion, _ := version.Parse(VERSION) + + if err = migration.Run(lastKnownVersion, currentVersion); err != nil { mudlog.Error("migration.Run()", "error", err) os.Exit(1) } - return // Default i18n localize folders if len(c.Translation.LanguagePaths) == 0 { c.Translation.LanguagePaths = []string{ @@ -127,12 +128,19 @@ func main() { mudlog.Info(`========================`) // - mudlog.Info(` ___ ____ _______ `) - mudlog.Info(` | \/ | | | | _ \ `) - mudlog.Info(` | . . | | | | | | | `) - mudlog.Info(` | |\/| | | | | | | | `) - mudlog.Info(` | | | | |_| | |/ / `) - mudlog.Info(` \_| |_/\___/|___/ `) + mudlog.Info(` _____ `) + mudlog.Info(` / ____| `) + mudlog.Info(`| | __ ___ `) + mudlog.Info(`| | |_ |/ _ \ `) + mudlog.Info(`| |__| | (_) | `) + mudlog.Info(` \_____|\___/ `) + mudlog.Info(` __ __ _ `) + mudlog.Info(`| \/ | | |`) + mudlog.Info(`| \ / |_ _ __| |`) + mudlog.Info(`| |\/| | | | |/ _' |`) + mudlog.Info(`| | | | |_| | (_| |`) + mudlog.Info(`|_| |_|\__,_|\__,_|`) + // mudlog.Info(`========================`) // From 71a78a2a6c7ccaf0727df3eeb265308084547984 Mon Sep 17 00:00:00 2001 From: Volte6 <143822+Volte6@users.noreply.github.com> Date: Mon, 23 Jun 2025 12:46:40 -0700 Subject: [PATCH 4/9] Fixing older/newer comparisons to use = operator --- internal/version/version.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/version/version.go b/internal/version/version.go index ce2ad515..cd7c0262 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -45,11 +45,11 @@ func (v Version) Compare(other Version) int { } func (v Version) IsNewerThan(other Version) bool { - return v.Compare(other) >= Newer + return v.Compare(other) == Newer } func (v Version) IsOlderThan(other Version) bool { - return v.Compare(other) <= Older + return v.Compare(other) == Older } func New(major int, minor int, patch int) Version { From 41cd377a2d55e6cbd76682521ba08a77b6d1ec6b Mon Sep 17 00:00:00 2001 From: Volte6 <143822+Volte6@users.noreply.github.com> Date: Mon, 23 Jun 2025 14:00:35 -0700 Subject: [PATCH 5/9] Better version comparisons, backup before migration and restore on failure. --- internal/migration/backup.go | 73 +++++++++++++++++++++++++++++++++ internal/migration/migration.go | 36 +++++++++++++--- internal/version/version.go | 4 ++ 3 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 internal/migration/backup.go diff --git a/internal/migration/backup.go b/internal/migration/backup.go new file mode 100644 index 00000000..1d8ea063 --- /dev/null +++ b/internal/migration/backup.go @@ -0,0 +1,73 @@ +package migration + +import ( + "errors" + "io" + "io/fs" + "os" + "path/filepath" + + "github.com/GoMudEngine/GoMud/internal/configs" +) + +func datafilesBackup() (string, error) { + + tmpDir, err := os.MkdirTemp("", "datafiles_backup_*") + if err != nil { + return "", err + } + + c := configs.GetConfig() + datafilesFolder := string(c.FilePaths.DataFiles) + + err = copyDir(datafilesFolder, tmpDir) + if err != nil { + return "", err + } + + return tmpDir, nil +} + +func copyDir(src string, dst string) error { + return filepath.WalkDir(src, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + relPath, err := filepath.Rel(src, path) + if err != nil { + return err + } + + destPath := filepath.Join(dst, relPath) + + if d.IsDir() { + _, err := os.Stat(destPath) + if errors.Is(err, os.ErrNotExist) { + return os.MkdirAll(destPath, 0755) + } + return nil + } + + // It’s a file + return copyFile(path, destPath) + }) +} + +// CopyFile copies a single file +func copyFile(srcFile, dstFile string) error { + srcF, err := os.Open(srcFile) + if err != nil { + return err + } + defer srcF.Close() + + dstF, err := os.Create(dstFile) + if err != nil { + return err + } + defer dstF.Close() + + _, err = io.Copy(dstF, srcF) + return err +} diff --git a/internal/migration/migration.go b/internal/migration/migration.go index 722cec2b..b7df0c73 100644 --- a/internal/migration/migration.go +++ b/internal/migration/migration.go @@ -1,29 +1,55 @@ package migration import ( + "fmt" + "os" + "github.com/GoMudEngine/GoMud/internal/configs" "github.com/GoMudEngine/GoMud/internal/version" ) -func Run(lastConfigVersion version.Version, serverVersion version.Version) error { +func Run(lastConfigVersion version.Version, serverVersion version.Version) (err error) { + + if lastConfigVersion.IsEqualTo(serverVersion) { + return nil + } // // Note: Follow this pattern and keep these version upgrades in order of lowest to greatest to avoid problems + // Note2: Take care to not shadow the err variable, since it is used in defer // + var backupFolder string + + // This defer checks whether an error is present before returning + // If so, restores backup. + defer func() { + if err != nil && backupFolder != `` { + fmt.Println("OOPS", err) + copyDir(backupFolder, string(configs.GetFilePathsConfig().DataFiles)) + } + os.RemoveAll(backupFolder) + }() + + backupFolder, err = datafilesBackup() + if err != nil { + err = fmt.Errorf(`could not backup datafiles: %w`, err) + return + } // 0.0.0 -> 1.0.0 if lastConfigVersion.IsOlderThan(version.New(1, 0, 0)) { - if err := migrate_RoomZoneConfig(); err != nil { - return err + err = migrate_RoomZoneConfig() + if err != nil { + return } } // - // Finally, update to the version this migration is for + // Finally, since successful, update to the version this migration is for // configs.SetVal(`Server.CurrentVersion`, serverVersion.String()) - return nil + return } diff --git a/internal/version/version.go b/internal/version/version.go index cd7c0262..a687a7ff 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -52,6 +52,10 @@ func (v Version) IsOlderThan(other Version) bool { return v.Compare(other) == Older } +func (v Version) IsEqualTo(other Version) bool { + return v.Compare(other) == Equal +} + func New(major int, minor int, patch int) Version { return Version{major, minor, patch} } From a91331ed6e223138db260f73a45a6661ed6305cb Mon Sep 17 00:00:00 2001 From: Volte6 <143822+Volte6@users.noreply.github.com> Date: Wed, 25 Jun 2025 13:06:26 -0700 Subject: [PATCH 6/9] Cleaned up, added comments, added check for zone-config.yaml --- internal/migration/1.0.0.go | 97 +++++++++++++++++++++++++++++---- internal/migration/migration.go | 61 ++++++++++++--------- 2 files changed, 121 insertions(+), 37 deletions(-) diff --git a/internal/migration/1.0.0.go b/internal/migration/1.0.0.go index 7b55ea15..ac60a826 100644 --- a/internal/migration/1.0.0.go +++ b/internal/migration/1.0.0.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" "regexp" + "strings" "github.com/GoMudEngine/GoMud/internal/configs" "github.com/GoMudEngine/GoMud/internal/mudlog" @@ -48,27 +49,85 @@ func migrate_RoomZoneConfig() error { return err } + existingZoneFiles := map[string]struct{}{} + // We only care about room files, so ###.yaml (possible negative) re := regexp.MustCompile(`^[\-0-9]+\.yaml$`) for _, path := range matches { - filename := filepath.Base(path) - if !re.MatchString(filename) { + // + // Must look like a room yaml file: + // 1.yaml + // 123.yaml + // -83.yaml + // etc. + // + + if !re.MatchString(filepath.Base(path)) { continue } + // + // strip the filename form the room file and replace with zone-config.yaml + // to get the path to the zone-config.yaml + // + zoneFilePath := filepath.Join(filepath.Dir(path), "zone-config.yaml") + + // + // The following checks whether the zone config file already exists + // We will leave the config data in the room data file if the zone-config.yaml is already present. + // It should be inert if present, since it is not unmarshalled into anything in current code. + // + + // Check whether zone file already is tracked as existing, if found, skip. + if _, ok := existingZoneFiles[zoneFilePath]; ok { + continue + } + + _, err = os.Stat(zoneFilePath) + if err == nil { + // Mark zone file as existing, skip further processing. + existingZoneFiles[zoneFilePath] = struct{}{} + continue + } + + // + // End check for existing zone-config.yaml + // After this point, we will unmarshal the yaml file into a generic map structure. + // This allows us to examine the data in the yaml file, particularly the "zoneconfig" node + // since the ZoneConfig field has been removed from the rooms.Room struct + // We can de-populate the field, move it, and re-write the yaml back to the original room template file. + // The downside to this method is that being a map, the fields will be read/written in a non-deterministic manner, + // So the room yaml file field orders may be written in a random order. + // Because of this, and as a final fix, we will finally marshal/unmarshal into the proper room struct from the map data + // Allowing us to write the data in an expected ordered form. + // + data, err := os.ReadFile(path) if err != nil { return err } - filedata := map[string]any{} + // + // First do a simple check for the field name in the text file. + // We know the way the field will appear: "zoneconfig:" + // This avoids having to unmarshal the struct and search that way, unnecessarily. + // + if !strings.Contains(string(data), "zoneconfig:") { + continue + } + // + // Unmarshal the entire yaml file into a map + // This will let us further examine the data, modify it, etc. + // + filedata := map[string]any{} err = yaml.Unmarshal(data, &filedata) if err != nil { return fmt.Errorf("failed to parse YAML: %w", err) } + // Make sure that the zoneconfig key is present and populated if filedata[`zoneconfig`] == nil { continue } @@ -82,7 +141,13 @@ func migrate_RoomZoneConfig() error { mudlog.Info("Migration 1.0.0", "file", path, "message", "isolating zoneconfig data") + // // Isolate the zoneconfig and write it to its own zone-config.yaml file + // We'll marshal just the zoneconfig data, get its bytes, then unmarshal it into + // the desired target structure. + // Some fields have changed or are missing due to some slight differences in the new struct + // so we'll also try and reconcile some of that by pulling from the core room definition + // zoneBytes, err := yaml.Marshal(filedata[`zoneconfig`]) if err != nil { return err @@ -106,24 +171,35 @@ func migrate_RoomZoneConfig() error { } } + mudlog.Info("Migration 1.0.0", "file", path, "message", "writing "+zoneFilePath) + + // + // Write the zone data to the zone-config.yaml path + // We'll just use whatever permissions were set in the room file for this file. + // zoneFileBytes, err := yaml.Marshal(zoneDataStruct) if err != nil { return err } - - zoneFilePath := filepath.Join(filepath.Dir(path), "zone-config.yaml") - - mudlog.Info("Migration 1.0.0", "file", path, "message", "writing "+zoneFilePath) - if err := os.WriteFile(zoneFilePath, zoneFileBytes, roomFileInfo.Mode().Perm()); err != nil { return err } - // Now clear "zoneconfig" and write the room data back - filedata[`zoneconfig`] = nil + // Mark zone file as existing + existingZoneFiles[zoneFilePath] = struct{}{} mudlog.Info("Migration 1.0.0", "file", path, "message", "writing modified room data") + // + // Now clear the "zoneconfig" node from the room data. + // The data will be in a random order if we just write this back to the room yaml file, + // so we'll take the extract step of marshalling the room data from the map into a string, + // and then unmarshal it into the actual target rooms.Room{} struct. + // This way, when writing to a file, it'll be in the typical field order according to the struct + // field order. + // + delete(filedata, `zoneconfig`) + // First marshal the modified room data into bytes modifiedRoomBytes, err := yaml.Marshal(filedata) if err != nil { @@ -142,6 +218,7 @@ func migrate_RoomZoneConfig() error { return err } + // Again, we'll just use the rooms original permissions when writing. if err := os.WriteFile(path, modifiedRoomBytes, roomFileInfo.Mode().Perm()); err != nil { return err } diff --git a/internal/migration/migration.go b/internal/migration/migration.go index b7df0c73..40b01fad 100644 --- a/internal/migration/migration.go +++ b/internal/migration/migration.go @@ -8,42 +8,49 @@ import ( "github.com/GoMudEngine/GoMud/internal/version" ) -func Run(lastConfigVersion version.Version, serverVersion version.Version) (err error) { +// Migration code goes here. +// They should be put in the order of oldest to newest and follow the pattern as below +func doAllMigrations(lastConfigVersion version.Version) error { + // 0.0.0 -> 1.0.0 + if lastConfigVersion.IsOlderThan(version.New(1, 0, 0)) { + + if err := migrate_RoomZoneConfig(); err != nil { + return err + } + + } + + return nil +} + +// Entrypoint for migrations. +// This is run on server start-up, after config files are loaded. +// NOTE: This means migrations that modify config files themselves would need special consideration +func Run(lastConfigVersion version.Version, serverVersion version.Version) error { + + // + // If already up to speed on version, we don't really need to do anything. + // if lastConfigVersion.IsEqualTo(serverVersion) { return nil } // - // Note: Follow this pattern and keep these version upgrades in order of lowest to greatest to avoid problems - // Note2: Take care to not shadow the err variable, since it is used in defer + // Start by making a backup of all datafiles. // - var backupFolder string - - // This defer checks whether an error is present before returning - // If so, restores backup. - defer func() { - if err != nil && backupFolder != `` { - fmt.Println("OOPS", err) - copyDir(backupFolder, string(configs.GetFilePathsConfig().DataFiles)) - } - os.RemoveAll(backupFolder) - }() - - backupFolder, err = datafilesBackup() + backupFolder, err := datafilesBackup() if err != nil { - err = fmt.Errorf(`could not backup datafiles: %w`, err) - return + return fmt.Errorf(`could not backup datafiles: %w`, err) } + defer os.RemoveAll(backupFolder) - // 0.0.0 -> 1.0.0 - if lastConfigVersion.IsOlderThan(version.New(1, 0, 0)) { - - err = migrate_RoomZoneConfig() - if err != nil { - return - } - + // + // If an error occured, restore backup + // + if err := doAllMigrations(lastConfigVersion); err != nil { + copyDir(backupFolder, string(configs.GetFilePathsConfig().DataFiles)) + return err } // @@ -51,5 +58,5 @@ func Run(lastConfigVersion version.Version, serverVersion version.Version) (err // configs.SetVal(`Server.CurrentVersion`, serverVersion.String()) - return + return nil } From 923b53aa60b6d1606a9aeb2f016f6170d6794c25 Mon Sep 17 00:00:00 2001 From: Volte6 <143822+Volte6@users.noreply.github.com> Date: Wed, 25 Jun 2025 15:32:29 -0700 Subject: [PATCH 7/9] adding --version flag to command line to get version --- internal/flags/flags.go | 10 +++++++++- main.go | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 357cce94..ca191ade 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -11,13 +11,21 @@ import ( "github.com/GoMudEngine/GoMud/internal/mudlog" ) -func HandleFlags() { +func HandleFlags(serverVersion string) { + var portsearch string + var showVersion bool flag.StringVar(&portsearch, "port-search", "", "Search for the first 10 open ports: -port-search=30000-40000") + flag.BoolVar(&showVersion, "version", false, "Display the current binary version") flag.Parse() + if showVersion { + fmt.Println(serverVersion) + os.Exit(0) + } + if portsearch != `` { doPortSearch(portsearch) os.Exit(0) diff --git a/main.go b/main.go index ca9dbcd2..669a6149 100644 --- a/main.go +++ b/main.go @@ -100,7 +100,7 @@ func main() { os.Getenv(`LOG_NOCOLOR`) == ``, ) - flags.HandleFlags() + flags.HandleFlags(VERSION) configs.ReloadConfig() c := configs.GetConfig() From 522498f9d206bfc7381a6776c05f8cc3e74c59b2 Mon Sep 17 00:00:00 2001 From: Volte6 <143822+Volte6@users.noreply.github.com> Date: Wed, 25 Jun 2025 15:36:24 -0700 Subject: [PATCH 8/9] Setting version to 0.9.1 --- internal/migration/migration.go | 4 ++-- main.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/migration/migration.go b/internal/migration/migration.go index 40b01fad..188f01c5 100644 --- a/internal/migration/migration.go +++ b/internal/migration/migration.go @@ -12,8 +12,8 @@ import ( // They should be put in the order of oldest to newest and follow the pattern as below func doAllMigrations(lastConfigVersion version.Version) error { - // 0.0.0 -> 1.0.0 - if lastConfigVersion.IsOlderThan(version.New(1, 0, 0)) { + // 0.0.0 -> 0.9.1 + if lastConfigVersion.IsOlderThan(version.New(0, 9, 1)) { if err := migrate_RoomZoneConfig(); err != nil { return err diff --git a/main.go b/main.go index 669a6149..eeb241ba 100644 --- a/main.go +++ b/main.go @@ -63,7 +63,7 @@ import ( // When updating this version: // 1. Expect to update the github release version // 2. Consider whether any migration code is needed for breaking changes, particularly in datafiles (see internal/migration) -const VERSION = "1.0.0" +const VERSION = "0.9.1" var ( sigChan = make(chan os.Signal, 1) From 4a6fb52c1cfeee26393227ff0be64c5e859321e7 Mon Sep 17 00:00:00 2001 From: Volte6 <143822+Volte6@users.noreply.github.com> Date: Wed, 25 Jun 2025 15:38:56 -0700 Subject: [PATCH 9/9] Renaming 1.0.0 to 0.9.1 --- internal/migration/{1.0.0.go => 0.9.1.go} | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) rename internal/migration/{1.0.0.go => 0.9.1.go} (95%) diff --git a/internal/migration/1.0.0.go b/internal/migration/0.9.1.go similarity index 95% rename from internal/migration/1.0.0.go rename to internal/migration/0.9.1.go index ac60a826..0356d076 100644 --- a/internal/migration/1.0.0.go +++ b/internal/migration/0.9.1.go @@ -19,7 +19,7 @@ import ( // If found, the data is moved to a zone-config.yaml file, and the ZoneConfig data in the Room datafile is removed. func migrate_RoomZoneConfig() error { - // This struct is how ZoneConfig looked as of 1.0.0 + // This struct is how ZoneConfig looked as of 0.9.1 // Since we will be upgrading an older version to this format, use a copy of the struct from that period // To ensure we aren't using a struct that has changed over time type zoneConfig_1_0_0 struct { @@ -132,14 +132,14 @@ func migrate_RoomZoneConfig() error { continue } - mudlog.Info("Migration 1.0.0", "file", path, "message", "migrating zoneconfig from room data file to zone-config.yaml") + mudlog.Info("Migration 0.9.1", "file", path, "message", "migrating zoneconfig from room data file to zone-config.yaml") // // From here on out, this code migrates zoneconfig data out of room file and into zone-config.yaml // roomFileInfo, _ := os.Stat(path) - mudlog.Info("Migration 1.0.0", "file", path, "message", "isolating zoneconfig data") + mudlog.Info("Migration 0.9.1", "file", path, "message", "isolating zoneconfig data") // // Isolate the zoneconfig and write it to its own zone-config.yaml file @@ -171,7 +171,7 @@ func migrate_RoomZoneConfig() error { } } - mudlog.Info("Migration 1.0.0", "file", path, "message", "writing "+zoneFilePath) + mudlog.Info("Migration 0.9.1", "file", path, "message", "writing "+zoneFilePath) // // Write the zone data to the zone-config.yaml path @@ -188,7 +188,7 @@ func migrate_RoomZoneConfig() error { // Mark zone file as existing existingZoneFiles[zoneFilePath] = struct{}{} - mudlog.Info("Migration 1.0.0", "file", path, "message", "writing modified room data") + mudlog.Info("Migration 0.9.1", "file", path, "message", "writing modified room data") // // Now clear the "zoneconfig" node from the room data. @@ -223,7 +223,7 @@ func migrate_RoomZoneConfig() error { return err } - mudlog.Info("Migration 1.0.0", "file", path, "message", "successfully updated") + mudlog.Info("Migration 0.9.1", "file", path, "message", "successfully updated") }