mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 07:40:43 +00:00
Add format:map-generator command (#2563)
I'd like to submit some PRs in the future to add additional options for logging and debugging to the map-generator, however I don't want to introduce any code style changes in feature requests. ## Description: I noticed there was not any style guide or formatting being done for the go files, and went with the simplest built-in approach w/ [go fmt](https://go.dev/blog/gofmt) - Adds a `format:map-generator` npm command that will run the default `go fmt` in the `map-generator` directory to format the go code. - Runs the formatter and commits the changes to `main.go` and `map_generator.go` - Updates the `map-generator` README: - Updated location for generated files - Updated Links to use markdown links - Add `info.json` example - Add in-game enabling instructions with list of files to modify - Add development tools section with format command ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: tidwell
This commit is contained in:
+40
-5
@@ -10,24 +10,59 @@ This is a tool to generate map files for OpenFront.
|
||||
|
||||
## Creating a new map
|
||||
|
||||
1. Create a new folder in assets/maps/<map_name>
|
||||
1. Create a new folder in `assets/maps/<map_name>`
|
||||
2. Create image.png
|
||||
3. Create info.json with name and countries
|
||||
4. Add the map name in main.go
|
||||
5. Run the generator: `go run .`
|
||||
6. Find the output folder at generated/maps/<map_name>
|
||||
6. Find the output folder at `../resources/maps/<map_name>`
|
||||
|
||||
## Create image.png
|
||||
|
||||
1. Download world map (warning very large file) https://drive.google.com/file/d/1W2oMPj1L5zWRyPhh8LfmnY3_kve-FBR2/view?usp=sharing
|
||||
2. Crop the file (recommend Gimp), we recommend roughly 2 million pixels for performance reasons. Do not go over 4 million pixels.
|
||||
1. [Download world map (warning very large file)](https://drive.google.com/file/d/1W2oMPj1L5zWRyPhh8LfmnY3_kve-FBR2/view?usp=sharing)
|
||||
2. Crop the file (recommend Gimp)
|
||||
|
||||
- We recommend roughly 2 million pixels for performance reasons
|
||||
- Do not go over 4 million pixels.
|
||||
|
||||
## Create info.json
|
||||
|
||||
- Look at existing info.json for structure
|
||||
- Use country codes found here: https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes
|
||||
- [Use country codes found here](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes)
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "MySampleMap",
|
||||
"nations": [
|
||||
{
|
||||
"coordinates": [396, 364],
|
||||
"name": "United States",
|
||||
"strength": 3,
|
||||
"flag": "us"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## To Enable In-Game
|
||||
|
||||
- Add a translation for the map name to `resources/lang/en.json`
|
||||
- Add the MapDescription `src/client/components/Maps.ts`
|
||||
- Add the numPlayersConfig `src/core/configuration/DefaultConfig.ts`
|
||||
- Add the GameMapType `src/core/game/Game.ts`
|
||||
- To add to the map playlist, modify `src/server/MapPlaylist.ts`
|
||||
|
||||
## Notes
|
||||
|
||||
- Islands smaller than 30 tiles (pixels) are automatically removed by the script.
|
||||
- Bodies of water smaller than 200 tiles (pixels) are also removed.
|
||||
|
||||
## 🛠️ Development Tools
|
||||
|
||||
- **Format map-generator code**:
|
||||
|
||||
```bash
|
||||
go fmt .
|
||||
```
|
||||
|
||||
+12
-13
@@ -71,13 +71,12 @@ func inputMapDir(isTest bool) (string, error) {
|
||||
return "", fmt.Errorf("failed to get working directory: %w", err)
|
||||
}
|
||||
if isTest {
|
||||
return filepath.Join(cwd, "assets", "test_maps"), nil
|
||||
return filepath.Join(cwd, "assets", "test_maps"), nil
|
||||
} else {
|
||||
return filepath.Join(cwd, "assets", "maps"), nil
|
||||
return filepath.Join(cwd, "assets", "maps"), nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func processMap(name string, isTest bool) error {
|
||||
outputMapBaseDir, err := outputMapDir(isTest)
|
||||
if err != nil {
|
||||
@@ -119,18 +118,18 @@ func processMap(name string, isTest bool) error {
|
||||
}
|
||||
|
||||
manifest["map"] = map[string]interface{}{
|
||||
"width": result.Map.Width,
|
||||
"height": result.Map.Height,
|
||||
"width": result.Map.Width,
|
||||
"height": result.Map.Height,
|
||||
"num_land_tiles": result.Map.NumLandTiles,
|
||||
}
|
||||
}
|
||||
manifest["map4x"] = map[string]interface{}{
|
||||
"width": result.Map4x.Width,
|
||||
"height": result.Map4x.Height,
|
||||
"width": result.Map4x.Width,
|
||||
"height": result.Map4x.Height,
|
||||
"num_land_tiles": result.Map4x.NumLandTiles,
|
||||
}
|
||||
manifest["map16x"] = map[string]interface{}{
|
||||
"width": result.Map16x.Width,
|
||||
"height": result.Map16x.Height,
|
||||
"width": result.Map16x.Width,
|
||||
"height": result.Map16x.Height,
|
||||
"num_land_tiles": result.Map16x.NumLandTiles,
|
||||
}
|
||||
|
||||
@@ -150,13 +149,13 @@ func processMap(name string, isTest bool) error {
|
||||
if err := os.WriteFile(filepath.Join(mapDir, "thumbnail.webp"), result.Thumbnail, 0644); err != nil {
|
||||
return fmt.Errorf("failed to write thumbnail for %s: %w", name, err)
|
||||
}
|
||||
|
||||
|
||||
// Serialize the updated manifest to JSON
|
||||
updatedManifest, err := json.MarshalIndent(manifest, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to serialize manifest for %s: %w", name, err)
|
||||
}
|
||||
|
||||
|
||||
if err := os.WriteFile(filepath.Join(mapDir, "manifest.json"), updatedManifest, 0644); err != nil {
|
||||
return fmt.Errorf("failed to write manifest for %s: %w", name, err)
|
||||
}
|
||||
@@ -196,6 +195,6 @@ func main() {
|
||||
if err := loadTerrainMaps(); err != nil {
|
||||
log.Fatalf("Error generating terrain maps: %v", err)
|
||||
}
|
||||
|
||||
|
||||
fmt.Println("Terrain maps generated successfully")
|
||||
}
|
||||
|
||||
+106
-106
@@ -43,20 +43,20 @@ type Terrain struct {
|
||||
|
||||
type MapResult struct {
|
||||
Thumbnail []byte
|
||||
Map MapInfo
|
||||
Map4x MapInfo
|
||||
Map16x MapInfo
|
||||
Map MapInfo
|
||||
Map4x MapInfo
|
||||
Map16x MapInfo
|
||||
}
|
||||
|
||||
type MapInfo struct {
|
||||
Data []byte
|
||||
Width int
|
||||
Height int
|
||||
Data []byte
|
||||
Width int
|
||||
Height int
|
||||
NumLandTiles int
|
||||
}
|
||||
|
||||
type GeneratorArgs struct {
|
||||
Name string
|
||||
Name string
|
||||
ImageBuffer []byte
|
||||
RemoveSmall bool
|
||||
}
|
||||
@@ -96,7 +96,7 @@ func GenerateMap(args GeneratorArgs) (MapResult, error) {
|
||||
} else {
|
||||
// Land
|
||||
terrain[x][y] = Terrain{Type: Land}
|
||||
|
||||
|
||||
// Calculate magnitude from blue channel (140-200 range)
|
||||
mag := math.Min(200, math.Max(140, float64(blue))) - 140
|
||||
terrain[x][y].Magnitude = mag / 2
|
||||
@@ -108,11 +108,11 @@ func GenerateMap(args GeneratorArgs) (MapResult, error) {
|
||||
processWater(terrain, args.RemoveSmall)
|
||||
|
||||
terrain4x := createMiniMap(terrain)
|
||||
processWater(terrain4x, false)
|
||||
|
||||
processWater(terrain4x, false)
|
||||
|
||||
terrain16x := createMiniMap(terrain4x)
|
||||
processWater(terrain16x, false)
|
||||
|
||||
|
||||
thumb := createMapThumbnail(terrain4x, 0.5)
|
||||
webp, err := convertToWebP(ThumbData{
|
||||
Data: thumb.Pix,
|
||||
@@ -129,21 +129,21 @@ func GenerateMap(args GeneratorArgs) (MapResult, error) {
|
||||
|
||||
return MapResult{
|
||||
Map: MapInfo{
|
||||
Data: mapData,
|
||||
Width: width,
|
||||
Height: height,
|
||||
Data: mapData,
|
||||
Width: width,
|
||||
Height: height,
|
||||
NumLandTiles: mapNumLandTiles,
|
||||
},
|
||||
Map4x: MapInfo{
|
||||
Data: mapData4x,
|
||||
Width: width / 2,
|
||||
Height: height / 2,
|
||||
Data: mapData4x,
|
||||
Width: width / 2,
|
||||
Height: height / 2,
|
||||
NumLandTiles: numLandTiles4x,
|
||||
},
|
||||
Map16x: MapInfo{
|
||||
Data: mapData16x,
|
||||
Width: width / 4,
|
||||
Height: height / 4,
|
||||
Data: mapData16x,
|
||||
Width: width / 4,
|
||||
Height: height / 4,
|
||||
NumLandTiles: numLandTiles16x,
|
||||
},
|
||||
Thumbnail: webp,
|
||||
@@ -153,13 +153,13 @@ func GenerateMap(args GeneratorArgs) (MapResult, error) {
|
||||
func convertToWebP(thumb ThumbData) ([]byte, error) {
|
||||
// Create RGBA image from raw data
|
||||
img := image.NewRGBA(image.Rect(0, 0, thumb.Width, thumb.Height))
|
||||
|
||||
|
||||
// Copy the raw RGBA data
|
||||
if len(thumb.Data) != thumb.Width*thumb.Height*4 {
|
||||
return nil, fmt.Errorf("invalid thumb data length: expected %d, got %d",
|
||||
return nil, fmt.Errorf("invalid thumb data length: expected %d, got %d",
|
||||
thumb.Width*thumb.Height*4, len(thumb.Data))
|
||||
}
|
||||
|
||||
|
||||
copy(img.Pix, thumb.Data)
|
||||
|
||||
// Encode as WebP with quality 45 (equivalent to the JavaScript version)
|
||||
@@ -174,10 +174,10 @@ func convertToWebP(thumb ThumbData) ([]byte, error) {
|
||||
func createMiniMap(tm [][]Terrain) [][]Terrain {
|
||||
width := len(tm)
|
||||
height := len(tm[0])
|
||||
|
||||
|
||||
miniWidth := width / 2
|
||||
miniHeight := height / 2
|
||||
|
||||
|
||||
miniMap := make([][]Terrain, miniWidth)
|
||||
for x := range miniMap {
|
||||
miniMap[x] = make([]Terrain, miniHeight)
|
||||
@@ -187,7 +187,7 @@ func createMiniMap(tm [][]Terrain) [][]Terrain {
|
||||
for y := 0; y < height; y++ {
|
||||
miniX := x / 2
|
||||
miniY := y / 2
|
||||
|
||||
|
||||
if miniX < miniWidth && miniY < miniHeight {
|
||||
// If any of the 4 tiles has water, mini tile is water
|
||||
if miniMap[miniX][miniY].Type != Water {
|
||||
@@ -196,7 +196,7 @@ func createMiniMap(tm [][]Terrain) [][]Terrain {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return miniMap
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ func processShore(terrain [][]Terrain) []Coord {
|
||||
for y := 0; y < height; y++ {
|
||||
tile := &terrain[x][y]
|
||||
neighbors := getNeighbors(x, y, terrain)
|
||||
|
||||
|
||||
if tile.Type == Land {
|
||||
// Land tile adjacent to water is shoreline
|
||||
for _, n := range neighbors {
|
||||
@@ -231,47 +231,47 @@ func processShore(terrain [][]Terrain) []Coord {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return shorelineWaters
|
||||
}
|
||||
|
||||
func processDistToLand(shorelineWaters []Coord, terrain [][]Terrain) {
|
||||
log.Println("Setting Water tiles magnitude = Manhattan distance from nearest land")
|
||||
|
||||
|
||||
width := len(terrain)
|
||||
height := len(terrain[0])
|
||||
|
||||
|
||||
visited := make([][]bool, width)
|
||||
for x := range visited {
|
||||
visited[x] = make([]bool, height)
|
||||
}
|
||||
|
||||
|
||||
type queueItem struct {
|
||||
x, y, dist int
|
||||
}
|
||||
|
||||
|
||||
queue := make([]queueItem, 0)
|
||||
|
||||
|
||||
// Initialize queue with shoreline waters
|
||||
for _, coord := range shorelineWaters {
|
||||
queue = append(queue, queueItem{x: coord.X, y: coord.Y, dist: 0})
|
||||
visited[coord.X][coord.Y] = true
|
||||
terrain[coord.X][coord.Y].Magnitude = 0
|
||||
}
|
||||
|
||||
|
||||
directions := []Coord{{0, 1}, {1, 0}, {0, -1}, {-1, 0}}
|
||||
|
||||
|
||||
for len(queue) > 0 {
|
||||
current := queue[0]
|
||||
queue = queue[1:]
|
||||
|
||||
|
||||
for _, dir := range directions {
|
||||
nx := current.x + dir.X
|
||||
ny := current.y + dir.Y
|
||||
|
||||
|
||||
if nx >= 0 && ny >= 0 && nx < width && ny < height &&
|
||||
!visited[nx][ny] && terrain[nx][ny].Type == Water {
|
||||
|
||||
|
||||
visited[nx][ny] = true
|
||||
terrain[nx][ny].Magnitude = float64(current.dist + 1)
|
||||
queue = append(queue, queueItem{x: nx, y: ny, dist: current.dist + 1})
|
||||
@@ -293,7 +293,7 @@ func getNeighborCoords(x, y int, terrain [][]Terrain) []Coord {
|
||||
width := len(terrain)
|
||||
height := len(terrain[0])
|
||||
var coords []Coord
|
||||
|
||||
|
||||
if x > 0 {
|
||||
coords = append(coords, Coord{X: x - 1, Y: y})
|
||||
}
|
||||
@@ -306,21 +306,21 @@ func getNeighborCoords(x, y int, terrain [][]Terrain) []Coord {
|
||||
if y < height-1 {
|
||||
coords = append(coords, Coord{X: x, Y: y + 1})
|
||||
}
|
||||
|
||||
|
||||
return coords
|
||||
}
|
||||
|
||||
func processWater(terrain [][]Terrain, removeSmall bool) {
|
||||
log.Println("Processing water bodies")
|
||||
visited := make(map[string]bool)
|
||||
|
||||
|
||||
type waterBody struct {
|
||||
coords []Coord
|
||||
size int
|
||||
}
|
||||
|
||||
|
||||
var waterBodies []waterBody
|
||||
|
||||
|
||||
// Find all distinct water bodies
|
||||
for x := 0; x < len(terrain); x++ {
|
||||
for y := 0; y < len(terrain[0]); y++ {
|
||||
@@ -329,7 +329,7 @@ func processWater(terrain [][]Terrain, removeSmall bool) {
|
||||
if visited[key] {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
coords := getArea(x, y, terrain, visited)
|
||||
waterBodies = append(waterBodies, waterBody{
|
||||
coords: coords,
|
||||
@@ -338,7 +338,7 @@ func processWater(terrain [][]Terrain, removeSmall bool) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Sort by size (largest first)
|
||||
for i := 0; i < len(waterBodies)-1; i++ {
|
||||
for j := i + 1; j < len(waterBodies); j++ {
|
||||
@@ -347,9 +347,9 @@ func processWater(terrain [][]Terrain, removeSmall bool) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
smallLakes := 0
|
||||
|
||||
|
||||
if len(waterBodies) > 0 {
|
||||
// Mark largest water body as ocean
|
||||
largestWaterBody := waterBodies[0]
|
||||
@@ -357,7 +357,7 @@ func processWater(terrain [][]Terrain, removeSmall bool) {
|
||||
terrain[coord.X][coord.Y].Ocean = true
|
||||
}
|
||||
log.Printf("Identified ocean with %d water tiles", largestWaterBody.size)
|
||||
|
||||
|
||||
if removeSmall {
|
||||
// Remove small water bodies
|
||||
log.Println("Searching for small water bodies for removal")
|
||||
@@ -370,10 +370,10 @@ func processWater(terrain [][]Terrain, removeSmall bool) {
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Printf("Identified and removed %d bodies of water smaller than %d tiles",
|
||||
log.Printf("Identified and removed %d bodies of water smaller than %d tiles",
|
||||
smallLakes, minLakeSize)
|
||||
}
|
||||
|
||||
|
||||
// Process shorelines and distances
|
||||
shorelineWaters := processShore(terrain)
|
||||
processDistToLand(shorelineWaters, terrain)
|
||||
@@ -386,25 +386,25 @@ func getArea(x, y int, terrain [][]Terrain, visited map[string]bool) []Coord {
|
||||
targetType := terrain[x][y].Type
|
||||
var area []Coord
|
||||
queue := []Coord{{X: x, Y: y}}
|
||||
|
||||
|
||||
for len(queue) > 0 {
|
||||
coord := queue[0]
|
||||
queue = queue[1:]
|
||||
|
||||
|
||||
key := fmt.Sprintf("%d,%d", coord.X, coord.Y)
|
||||
if visited[key] {
|
||||
continue
|
||||
}
|
||||
visited[key] = true
|
||||
|
||||
|
||||
if terrain[coord.X][coord.Y].Type == targetType {
|
||||
area = append(area, coord)
|
||||
|
||||
|
||||
neighborCoords := getNeighborCoords(coord.X, coord.Y, terrain)
|
||||
queue = append(queue, neighborCoords...)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return area
|
||||
}
|
||||
|
||||
@@ -412,16 +412,16 @@ func removeSmallIslands(terrain [][]Terrain, removeSmall bool) {
|
||||
if !removeSmall {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
visited := make(map[string]bool)
|
||||
|
||||
|
||||
type landBody struct {
|
||||
coords []Coord
|
||||
size int
|
||||
}
|
||||
|
||||
|
||||
var landBodies []landBody
|
||||
|
||||
|
||||
// Find all distinct land bodies
|
||||
for x := 0; x < len(terrain); x++ {
|
||||
for y := 0; y < len(terrain[0]); y++ {
|
||||
@@ -430,7 +430,7 @@ func removeSmallIslands(terrain [][]Terrain, removeSmall bool) {
|
||||
if visited[key] {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
coords := getArea(x, y, terrain, visited)
|
||||
landBodies = append(landBodies, landBody{
|
||||
coords: coords,
|
||||
@@ -439,9 +439,9 @@ func removeSmallIslands(terrain [][]Terrain, removeSmall bool) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
smallIslands := 0
|
||||
|
||||
|
||||
for _, body := range landBodies {
|
||||
if body.size < minIslandSize {
|
||||
smallIslands++
|
||||
@@ -451,8 +451,8 @@ func removeSmallIslands(terrain [][]Terrain, removeSmall bool) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Identified and removed %d islands smaller than %d tiles",
|
||||
|
||||
log.Printf("Identified and removed %d islands smaller than %d tiles",
|
||||
smallIslands, minIslandSize)
|
||||
}
|
||||
|
||||
@@ -461,12 +461,12 @@ func packTerrain(terrain [][]Terrain) (data []byte, numLandTiles int) {
|
||||
height := len(terrain[0])
|
||||
packedData := make([]byte, width*height)
|
||||
numLandTiles = 0
|
||||
|
||||
|
||||
for x := 0; x < width; x++ {
|
||||
for y := 0; y < height; y++ {
|
||||
tile := terrain[x][y]
|
||||
var packedByte byte = 0
|
||||
|
||||
|
||||
if tile.Type == Land {
|
||||
packedByte |= 0b10000000
|
||||
numLandTiles++
|
||||
@@ -477,46 +477,46 @@ func packTerrain(terrain [][]Terrain) (data []byte, numLandTiles int) {
|
||||
if tile.Ocean {
|
||||
packedByte |= 0b00100000
|
||||
}
|
||||
|
||||
|
||||
if tile.Type == Land {
|
||||
packedByte |= byte(math.Min(math.Ceil(tile.Magnitude), 31))
|
||||
} else {
|
||||
packedByte |= byte(math.Min(math.Ceil(tile.Magnitude/2), 31))
|
||||
}
|
||||
|
||||
|
||||
packedData[y*width+x] = packedByte
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
logBinaryAsBits(packedData, 8)
|
||||
return packedData, numLandTiles
|
||||
}
|
||||
|
||||
func createMapThumbnail(terrain [][]Terrain, quality float64) *image.RGBA {
|
||||
log.Println("Creating thumbnail")
|
||||
|
||||
|
||||
srcWidth := len(terrain)
|
||||
srcHeight := len(terrain[0])
|
||||
|
||||
|
||||
targetWidth := int(math.Max(1, math.Floor(float64(srcWidth)*quality)))
|
||||
targetHeight := int(math.Max(1, math.Floor(float64(srcHeight)*quality)))
|
||||
|
||||
|
||||
img := image.NewRGBA(image.Rect(0, 0, targetWidth, targetHeight))
|
||||
|
||||
|
||||
for x := 0; x < targetWidth; x++ {
|
||||
for y := 0; y < targetHeight; y++ {
|
||||
srcX := int(math.Floor(float64(x) / quality))
|
||||
srcY := int(math.Floor(float64(y) / quality))
|
||||
|
||||
|
||||
srcX = int(math.Min(float64(srcX), float64(srcWidth-1)))
|
||||
srcY = int(math.Min(float64(srcY), float64(srcHeight-1)))
|
||||
|
||||
|
||||
terrain := terrain[srcX][srcY]
|
||||
rgba := getThumbnailColor(terrain)
|
||||
img.Set(x, y, color.RGBA{R: rgba.R, G: rgba.G, B: rgba.B, A: rgba.A})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return img
|
||||
}
|
||||
|
||||
@@ -539,12 +539,12 @@ func getThumbnailColor(t Terrain) RGBA {
|
||||
A: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Shoreline land
|
||||
if t.Shoreline {
|
||||
return RGBA{R: 204, G: 203, B: 158, A: 255}
|
||||
}
|
||||
|
||||
|
||||
var adjRGB float64
|
||||
if t.Magnitude < 10 {
|
||||
// Plains
|
||||
@@ -580,7 +580,7 @@ func logBinaryAsBits(data []byte, length int) {
|
||||
if length > len(data) {
|
||||
length = len(data)
|
||||
}
|
||||
|
||||
|
||||
var bits string
|
||||
for i := 0; i < length; i++ {
|
||||
bits += fmt.Sprintf("%08b ", data[i])
|
||||
@@ -593,7 +593,7 @@ func createCombinedBinary(infoBuffer []byte, mapData []byte, miniMapData []byte)
|
||||
infoSize := len(infoBuffer)
|
||||
mapSize := len(mapData)
|
||||
miniMapSize := len(miniMapData)
|
||||
|
||||
|
||||
// Header structure:
|
||||
// Bytes 0-3: Version (1)
|
||||
// Bytes 4-7: Info section offset (always 28)
|
||||
@@ -602,35 +602,35 @@ func createCombinedBinary(infoBuffer []byte, mapData []byte, miniMapData []byte)
|
||||
// Bytes 16-19: Map section size
|
||||
// Bytes 20-23: MiniMap section offset
|
||||
// Bytes 24-27: MiniMap section size
|
||||
|
||||
|
||||
headerSize := 28
|
||||
infoOffset := headerSize
|
||||
mapOffset := infoOffset + infoSize
|
||||
miniMapOffset := mapOffset + mapSize
|
||||
|
||||
|
||||
totalSize := miniMapOffset + miniMapSize
|
||||
combined := make([]byte, totalSize)
|
||||
|
||||
|
||||
// Write version
|
||||
writeUint32(combined, 0, 1)
|
||||
|
||||
|
||||
// Write info section info
|
||||
writeUint32(combined, 4, uint32(infoOffset))
|
||||
writeUint32(combined, 8, uint32(infoSize))
|
||||
|
||||
|
||||
// Write map section info
|
||||
writeUint32(combined, 12, uint32(mapOffset))
|
||||
writeUint32(combined, 16, uint32(mapSize))
|
||||
|
||||
|
||||
// Write miniMap section info
|
||||
writeUint32(combined, 20, uint32(miniMapOffset))
|
||||
writeUint32(combined, 24, uint32(miniMapSize))
|
||||
|
||||
|
||||
// Copy data sections
|
||||
copy(combined[infoOffset:], infoBuffer)
|
||||
copy(combined[mapOffset:], mapData)
|
||||
copy(combined[miniMapOffset:], miniMapData)
|
||||
|
||||
|
||||
return combined
|
||||
}
|
||||
|
||||
@@ -649,38 +649,38 @@ func decodeCombinedBinary(data []byte) (*CombinedBinaryHeader, []byte, []byte, [
|
||||
if len(data) < 28 {
|
||||
return nil, nil, nil, nil, fmt.Errorf("data too short for header")
|
||||
}
|
||||
|
||||
|
||||
header := &CombinedBinaryHeader{
|
||||
Version: readUint32(data, 0),
|
||||
InfoOffset: readUint32(data, 4),
|
||||
InfoSize: readUint32(data, 8),
|
||||
MapOffset: readUint32(data, 12),
|
||||
MapSize: readUint32(data, 16),
|
||||
Version: readUint32(data, 0),
|
||||
InfoOffset: readUint32(data, 4),
|
||||
InfoSize: readUint32(data, 8),
|
||||
MapOffset: readUint32(data, 12),
|
||||
MapSize: readUint32(data, 16),
|
||||
MiniMapOffset: readUint32(data, 20),
|
||||
MiniMapSize: readUint32(data, 24),
|
||||
MiniMapSize: readUint32(data, 24),
|
||||
}
|
||||
|
||||
|
||||
// Validate offsets and sizes
|
||||
if header.InfoOffset+header.InfoSize > uint32(len(data)) ||
|
||||
header.MapOffset+header.MapSize > uint32(len(data)) ||
|
||||
header.MiniMapOffset+header.MiniMapSize > uint32(len(data)) {
|
||||
return nil, nil, nil, nil, fmt.Errorf("invalid offsets or sizes in header")
|
||||
}
|
||||
|
||||
|
||||
// Extract sections
|
||||
infoData := data[header.InfoOffset : header.InfoOffset+header.InfoSize]
|
||||
mapData := data[header.MapOffset : header.MapOffset+header.MapSize]
|
||||
miniMapData := data[header.MiniMapOffset : header.MiniMapOffset+header.MiniMapSize]
|
||||
|
||||
|
||||
return header, infoData, mapData, miniMapData, nil
|
||||
}
|
||||
|
||||
type CombinedBinaryHeader struct {
|
||||
Version uint32
|
||||
InfoOffset uint32
|
||||
InfoSize uint32
|
||||
MapOffset uint32
|
||||
MapSize uint32
|
||||
Version uint32
|
||||
InfoOffset uint32
|
||||
InfoSize uint32
|
||||
MapOffset uint32
|
||||
MapSize uint32
|
||||
MiniMapOffset uint32
|
||||
MiniMapSize uint32
|
||||
}
|
||||
MiniMapSize uint32
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"perf": "npx tsx tests/perf/*.ts",
|
||||
"test:coverage": "jest --coverage",
|
||||
"format": "prettier --ignore-unknown --write .",
|
||||
"format:map-generator": "cd map-generator && go fmt .",
|
||||
"lint": "eslint",
|
||||
"lint:fix": "eslint --fix",
|
||||
"prepare": "husky",
|
||||
|
||||
Reference in New Issue
Block a user