Created
October 9, 2016 20:09
-
-
Save jonas747/fb8814c9c8f1445dd79864dee360208d to your computer and use it in GitHub Desktop.
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
package main | |
import ( | |
"encoding/json" | |
"flag" | |
"fmt" | |
"github.com/fatih/color" | |
"github.com/hpcloud/tail" | |
"io/ioutil" | |
"math" | |
"os" | |
"strings" | |
"time" | |
) | |
const ( | |
VERSION = "0.1" | |
) | |
var ( | |
flagLogPath string | |
flagSplitFile string | |
BestTime *SavedTimes | |
) | |
func init() { | |
flag.StringVar(&flagLogPath, "l", "/home/jonas/.config/unity3d/Landfall/Clustertruck/Player.log", "Clusertruck log path") | |
flag.StringVar(&flagSplitFile, "s", "best_time.json", "Best time file") | |
flag.Parse() | |
} | |
type SavedTimes struct { | |
Times []*LevelTime `json:"times"` | |
} | |
func main() { | |
fmt.Println("Starting funsplit version", VERSION) | |
file, err := ioutil.ReadFile(flagSplitFile) | |
if err == nil { | |
err = json.Unmarshal(file, &BestTime) | |
if err == nil { | |
fmt.Println("Loaded best time of ", BestTime.Times[len(BestTime.Times)-1].End) | |
} | |
} else { | |
fmt.Println("Failed loading best time", err) | |
} | |
evts := make(chan interface{}) | |
go logReader(flagLogPath, evts) | |
go timer(evts) | |
select {} | |
} | |
type StopEvt struct{} | |
type DoneEvt struct{} | |
type LevelChangedEvt struct { | |
NewLevel string | |
} | |
func logReader(path string, evts chan interface{}) { | |
t, err := tail.TailFile(path, tail.Config{Follow: true}) | |
if err != nil { | |
fmt.Println(err) | |
os.Exit(1) | |
} | |
curLevel := "" | |
// Ignore the first second | |
started := time.Now() | |
for line := range t.Lines { | |
if line.Err != nil { | |
fmt.Println(err) | |
os.Exit(1) | |
} | |
if time.Since(started) < time.Second { | |
continue // skip untill we reach the new logs | |
} | |
if line.Text == "Uploading To Steam! To Leaderboard: 90" { | |
evts <- &DoneEvt{} | |
continue | |
} | |
split := strings.SplitN(line.Text, ":", 2) | |
if len(split) < 2 { | |
continue | |
} | |
if split[0] != "CURRENTLEVEL CHANGHED" { | |
continue | |
} | |
newLevel := strings.TrimSpace(split[1]) | |
if newLevel == curLevel { | |
continue | |
} | |
curLevel = newLevel | |
evts <- &LevelChangedEvt{NewLevel: newLevel} | |
} | |
} | |
type LevelTime struct { | |
End time.Duration `json:"end"` | |
Name string `json:"name"` | |
} | |
func timer(evts chan interface{}) { | |
ticker := time.NewTicker(time.Millisecond * 10) | |
splits := make([]*LevelTime, 0) | |
curLevel := "" | |
var started time.Time | |
for { | |
select { | |
case evt := <-evts: | |
switch t := evt.(type) { | |
case *StopEvt: | |
return | |
case *LevelChangedEvt: | |
if curLevel == "" { | |
started = time.Now() | |
splits = make([]*LevelTime, 0) | |
} else { | |
split := &LevelTime{End: time.Since(started), Name: "Level " + curLevel} | |
splits = append(splits, split) | |
} | |
curLevel = t.NewLevel | |
displaySegments(splits) | |
case *DoneEvt: | |
split := &LevelTime{End: time.Since(started), Name: "Level " + curLevel} | |
splits = append(splits, split) | |
curLevel = "" | |
maybeSaveTime(splits) | |
displaySegments(splits) | |
} | |
case <-ticker.C: | |
var curtime time.Duration | |
level := "Waiting" | |
if curLevel != "" { | |
curtime = time.Since(started) | |
level = curLevel | |
} | |
displayCurrentTime("Level "+level, curtime) | |
} | |
} | |
} | |
// Saves the time if it's the best one | |
func maybeSaveTime(times []*LevelTime) { | |
if BestTime != nil { | |
if times[len(times)-1].End > BestTime.Times[len(BestTime.Times)-1].End { | |
return // Not new best | |
} | |
} | |
// Wrap it up into a christmas present | |
wrapped := &SavedTimes{Times: times} | |
data, err := json.Marshal(wrapped) | |
if err != nil { | |
return | |
} | |
err = ioutil.WriteFile(flagSplitFile, data, 0755) | |
if err != nil { | |
panic(err) | |
} | |
BestTime = wrapped | |
} | |
func displayCurrentTime(current string, fullDuration time.Duration) { | |
fmt.Printf("%s: %s\r", current, formatDuration(fullDuration)) | |
} | |
func displaySegments(times []*LevelTime) { | |
out := "\033[2J\033[1;1H" | |
for i, v := range times { | |
stageTime := fmt.Sprintf("%-9s: %-11s", v.Name, formatDuration(v.End)) | |
if BestTime != nil && len(BestTime.Times) >= i { | |
diff := v.End - BestTime.Times[i].End | |
mod := "+" | |
col := color.New(color.FgHiRed) | |
if diff < 0 { | |
mod = "-" | |
diff *= -1 | |
col = color.New(color.FgHiGreen) | |
} | |
out += col.SprintfFunc()("%-25s ( %s %s )", stageTime, mod, formatDuration(diff)) | |
} else { | |
out += stageTime | |
} | |
out += "\n" | |
} | |
fmt.Print(out) | |
} | |
func formatDuration(d time.Duration) string { | |
seconds := d.Seconds() | |
minutes := int(math.Floor(seconds/60)) % 60 | |
hours := int(math.Floor((seconds / 60) / 60)) | |
secondsMillisecondsFull := int(math.Floor(seconds * 1000)) | |
secondsMillisecondsFull = secondsMillisecondsFull % 60000 | |
secondsMilliseconds := float64(secondsMillisecondsFull) / 1000 | |
out := "" | |
if hours > 0 { | |
out = fmt.Sprintf("%02d:%02d:%06.3f", hours, minutes, secondsMilliseconds) | |
} else if minutes > 0 { | |
out = fmt.Sprintf("%02d:%06.3f", minutes, secondsMilliseconds) | |
} else { | |
out = fmt.Sprintf("%06.3f", secondsMilliseconds) | |
} | |
return out | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment