-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from myjimnelson/0.4.0-release
0.4.0 release with GraphQL
- Loading branch information
Showing
10 changed files
with
1,100 additions
and
253 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
package civ3satgql | ||
|
||
import ( | ||
"encoding/json" | ||
"log" | ||
"net/http" | ||
"strconv" | ||
|
||
"github.com/graphql-go/graphql" | ||
"github.com/graphql-go/handler" | ||
"github.com/myjimnelson/c3sat/parseciv3" | ||
) | ||
|
||
type sectionType struct { | ||
name string | ||
offset int | ||
length int | ||
} | ||
|
||
type saveGameType struct { | ||
data []byte | ||
sections []sectionType | ||
} | ||
|
||
var saveGame saveGameType | ||
|
||
func findSections() { | ||
var i, count, offset int | ||
for i < len(saveGame.data) { | ||
// for i < 83000 { | ||
if saveGame.data[i] < 0x20 || saveGame.data[i] > 0x5a { | ||
count = 0 | ||
} else { | ||
if count == 0 { | ||
offset = i | ||
} | ||
count++ | ||
} | ||
i++ | ||
if count > 3 { | ||
count = 0 | ||
s := new(sectionType) | ||
s.offset = offset | ||
s.name = string(saveGame.data[offset:i]) | ||
saveGame.sections = append(saveGame.sections, *s) | ||
// fmt.Println(string(saveGame.data[offset:i]) + " " + strconv.Itoa(offset)) | ||
} | ||
} | ||
} | ||
|
||
// Handler wrapper to allow adding headers to all responses | ||
// concept yoinked from http://echorand.me/dissecting-golangs-handlerfunc-handle-and-defaultservemux.html | ||
func setHeaders(handler http.Handler) http.Handler { | ||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
// Set Origin headers for CORS | ||
// yoinked from http://stackoverflow.com/questions/12830095/setting-http-headers-in-golang Matt Bucci's answer | ||
if origin := r.Header.Get("Origin"); origin != "" { | ||
w.Header().Set("Access-Control-Allow-Origin", origin) | ||
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") | ||
w.Header().Set("Access-Control-Allow-Headers", | ||
"Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") | ||
} | ||
// Since we're dynamically setting origin, don't let it get cached | ||
w.Header().Set("Vary", "Origin") | ||
handler.ServeHTTP(w, r) | ||
}) | ||
} | ||
|
||
func Server(path string, bindAddress, bindPort string) error { | ||
var err error | ||
saveGame.data, _, err = parseciv3.ReadFile(path) | ||
if err != nil { | ||
return err | ||
} | ||
findSections() | ||
|
||
Schema, err := graphql.NewSchema(graphql.SchemaConfig{ | ||
Query: queryType, | ||
// Mutation: MutationType, | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// create a graphl-go HTTP handler | ||
graphQlHandler := handler.New(&handler.Config{ | ||
Schema: &Schema, | ||
Pretty: false, | ||
// GraphiQL provides simple web browser query interface pulled from Internet | ||
GraphiQL: false, | ||
// Playground provides fancier web browser query interface pulled from Internet | ||
Playground: true, | ||
}) | ||
|
||
http.Handle("/graphql", setHeaders(graphQlHandler)) | ||
log.Fatal(http.ListenAndServe(bindAddress+":"+bindPort, nil)) | ||
return nil | ||
} | ||
|
||
func Query(query, path string) (string, error) { | ||
var err error | ||
saveGame.data, _, err = parseciv3.ReadFile(path) | ||
if err != nil { | ||
return "", err | ||
} | ||
findSections() | ||
// fmt.Println(saveGame.sections[len(saveGame.sections)-1]) | ||
// saveGame.sections = []string{"hello", "there"} | ||
Schema, err := graphql.NewSchema(graphql.SchemaConfig{ | ||
Query: queryType, | ||
// Mutation: MutationType, | ||
}) | ||
if err != nil { | ||
return "", err | ||
} | ||
result := graphql.Do(graphql.Params{ | ||
Schema: Schema, | ||
RequestString: query, | ||
}) | ||
out, err := json.Marshal(result) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
// return hex.EncodeToString(saveGame[:4]), nil | ||
return string(out[:]), nil | ||
} | ||
|
||
// Adapting this from the parseciv3 module to pull data this module's way | ||
// To keep the existing seed command without having to gql everything | ||
// WorldSettings Returns the information needed to regenerate the map | ||
// presuming the map was originally generated by Civ3 and not later edited | ||
func WorldSettings(path string) ([][3]string, error) { | ||
var worldSize = [...]string{"Tiny", "Small", "Standard", "Large", "Huge", "Random"} | ||
// // "No Barbarians" is actually -1. To make code simpler, add one to value for array index | ||
var barbs = [...]string{"No Barbarians", "Sedentary", "Roaming", "Restless", "Raging", "Random"} | ||
var landMass = [...]string{"Archipelago", "Continents", "Pangea", "Random"} | ||
var oceanCoverage = [...]string{"80% Water", "70% Water", "60% Water", "Random"} | ||
var climate = [...]string{"Arid", "Normal", "Wet", "Random"} | ||
var temperature = [...]string{"Warm", "Temperate", "Cool", "Random"} | ||
var age = [...]string{"3 Billion", "4 Billion", "5 Billion", "Random"} | ||
var settings [][3]string | ||
var err error | ||
saveGame.data, _, err = parseciv3.ReadFile(path) | ||
if err != nil { | ||
return [][3]string{}, err | ||
} | ||
findSections() | ||
wrldSection, err := SectionOffset("WRLD", 1) | ||
if err != nil { | ||
return [][3]string{}, err | ||
} | ||
settings = [][3]string{ | ||
{"Setting", "Choice", "Result"}, | ||
{"World Seed", strconv.FormatUint(uint64(ReadInt32(wrldSection+170, Signed)), 10), ""}, | ||
{"World Size", worldSize[ReadInt32(wrldSection+234, Signed)], ""}, | ||
{"Barbarians", barbs[ReadInt32(wrldSection+194, Signed)+1], barbs[ReadInt32(wrldSection+198, Signed)+1]}, | ||
{"Land Mass", landMass[ReadInt32(wrldSection+202, Signed)], landMass[ReadInt32(wrldSection+206, Signed)]}, | ||
{"Water Coverage", oceanCoverage[ReadInt32(wrldSection+210, Signed)], oceanCoverage[ReadInt32(wrldSection+214, Signed)]}, | ||
{"Climate", climate[ReadInt32(wrldSection+186, Signed)], climate[ReadInt32(wrldSection+190, Signed)]}, | ||
{"Temperature", temperature[ReadInt32(wrldSection+218, Signed)], temperature[ReadInt32(wrldSection+222, Signed)]}, | ||
{"Age", age[ReadInt32(wrldSection+226, Signed)], age[ReadInt32(wrldSection+230, Signed)]}, | ||
} | ||
return settings, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package civ3satgql | ||
|
||
import ( | ||
"errors" | ||
"strconv" | ||
) | ||
|
||
// to make calling functions readable | ||
const Signed = true | ||
const Unsigned = false | ||
|
||
func ReadInt32(offset int, signed bool) int { | ||
n := int(saveGame.data[offset]) + | ||
int(saveGame.data[offset+1])*0x100 + | ||
int(saveGame.data[offset+2])*0x10000 + | ||
int(saveGame.data[offset+3])*0x1000000 | ||
if signed && n > 0x80000000 { | ||
n = n - 0x80000000 | ||
} | ||
return n | ||
} | ||
|
||
func ReadInt16(offset int, signed bool) int { | ||
n := int(saveGame.data[offset]) + | ||
int(saveGame.data[offset+1])*0x100 | ||
if signed && n > 0x8000 { | ||
n = n - 0x8000 | ||
} | ||
return n | ||
} | ||
|
||
func ReadInt8(offset int, signed bool) int { | ||
n := int(saveGame.data[offset]) | ||
if signed && n > 0x80 { | ||
n = n - 0x80 | ||
} | ||
return n | ||
} | ||
|
||
func SectionOffset(sectionName string, nth int) (int, error) { | ||
var i, n int | ||
for i < len(saveGame.sections) { | ||
if saveGame.sections[i].name == sectionName { | ||
n++ | ||
if n >= nth { | ||
return saveGame.sections[i].offset + len(sectionName), nil | ||
} | ||
} | ||
i++ | ||
} | ||
return -1, errors.New("Could not find " + strconv.Itoa(nth) + " section named " + sectionName) | ||
} |
Oops, something went wrong.