Skip to content

Instantly share code, notes, and snippets.

@jonas747
Created October 9, 2016 20:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jonas747/fb8814c9c8f1445dd79864dee360208d to your computer and use it in GitHub Desktop.
Save jonas747/fb8814c9c8f1445dd79864dee360208d to your computer and use it in GitHub Desktop.
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