about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--README.md51
-rw-r--r--bots/amaterasu.x86-32.asm35
-rw-r--r--bots/pancake.x86-32.asm7
-rw-r--r--go.mod1
-rw-r--r--go.sum2
-rwxr-xr-xrun.sh2
-rw-r--r--src/battle.go177
-rw-r--r--src/db.go6
-rw-r--r--src/main.go4
-rw-r--r--src/user.go16
-rw-r--r--templates/battleSingle.html266
-rw-r--r--templates/nav.html4
-rw-r--r--vendor/modules.txt2
14 files changed, 415 insertions, 160 deletions
diff --git a/.gitignore b/.gitignore
index f0b11c5..1d306c2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,6 @@
 main.db
 sessions.db
 server.log
-
 result/
+.DS_Store
 
diff --git a/README.md b/README.md
index 525915d..8d996a1 100644
--- a/README.md
+++ b/README.md
@@ -20,3 +20,54 @@ If you want to clone from git.emile.space, you can currently do so like this:
 git clone git://git.emile.space/r2wars-web.git
 git clone git://git.emile.space/hefe.git
 ```
+
+## Usage
+
+```
+; CGO_ENABLED=0 SESSION_KEY=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa go run ./src --help
+Usage of /var/folders/bt/2db5y4ds5yq2y9m29tt8g5dm0000gn/T/go-build1055665732/b001/exe/src:
+  -databasepath string
+    	The path to the main database (default "./main.db")
+  -h string
+    	The host to listen on (shorthand) (default "127.0.0.1")
+  -host string
+    	The host to listen on (default "127.0.0.1")
+  -logfilepath string
+    	The path to the log file (default "./server.log")
+  -p int
+    	The port to listen on (shorthand) (default 8080)
+  -port int
+    	The port to listen on (default 8080)
+  -sessiondbpath string
+    	The path to the session database (default "./sesions.db")
+  -templates string
+    	The path to the templates used (default "./templates")
+```
+
+## Architecture
+
+There are essentially the following objects which are all linked to each other (using a table joining their ids):
+
+- User
+  - You, the player
+- Bots
+  - The bots to be run within a battle
+- Battles
+  - An arena in which bots can be placed and run. Constraints on what bots can be added are defined here
+- Architectures
+  - The archs supported by r2 in order to be used by bots and battles
+- Bits
+  - The bits (8, 16, 32, 64) supported by r2 in order to be used by bots and battles
+
+## TODO
+
+- [ ] Add user creating battle as default owner
+- [ ] Allow adding other users as owners to battles
+- [ ] Implement submitting bots
+- [ ] Implement running the battle
+- [ ] Add a "start battle now" button
+- [ ] Add a "battle starts at this time" field into the battle
+- [ ] Figure out how time is stored and restored with the db
+- [ ] Do some magic to display the current fight backlog with all info
+- [ ] After having added a bot to a battle with the right arch, the arch can be changed
+      When updating the bot, make sure that it is still valid in all currently linked battles
diff --git a/bots/amaterasu.x86-32.asm b/bots/amaterasu.x86-32.asm
new file mode 100644
index 0000000..55d38df
--- /dev/null
+++ b/bots/amaterasu.x86-32.asm
@@ -0,0 +1,35 @@
+start:
+    mov ebp, 0x3e0
+    mov esp, 0x3e0
+    ; lea eax, [end + 0x20]
+    ; lea ebx, [start - 0x20]
+    mov eax, 0xffffffff
+    mov ebx, 0xffffffff
+    mov ecx, 0xffffffff
+    mov edx, 0xffffffff
+    mov edi, 0xffffffff
+    mov esi, 0xffffffff
+
+bot_loop:
+    ; cmp esp, eax
+    ; cmovbe esp, ebx
+
+    ; push 128 bytes at once
+    pushad
+    pushad
+    pushad
+    pushad
+
+    ; push again
+    pushad
+    pushad
+    pushad
+    pushad
+
+    ; jmp to beginning once again
+    cmp esp, 0x10
+    cmovz esp, ebp
+    jmp bot_loop
+
+end:
+    nop
\ No newline at end of file
diff --git a/bots/pancake.x86-32.asm b/bots/pancake.x86-32.asm
new file mode 100644
index 0000000..25eda04
--- /dev/null
+++ b/bots/pancake.x86-32.asm
@@ -0,0 +1,7 @@
+call rest
+rest:
+  pop esp
+rep:
+  add esp, 64
+  pusha
+  jmp rep
diff --git a/go.mod b/go.mod
index b897dd4..9a6722a 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,6 @@ require (
 	github.com/gorilla/securecookie v1.1.2
 	github.com/gorilla/sessions v1.3.0
 	github.com/radareorg/r2pipe-go v0.2.1
-	github.com/xiaoqidun/entps v1.32.0
 	golang.org/x/crypto v0.26.0
 	modernc.org/sqlite v1.32.0
 )
diff --git a/go.sum b/go.sum
index 5022658..6ba4f7d 100644
--- a/go.sum
+++ b/go.sum
@@ -28,8 +28,6 @@ github.com/radareorg/r2pipe-go v0.2.1 h1:2bVIi7qHPQCdjjngcJbQO2VHSqPaRBt5LUAMk2u
 github.com/radareorg/r2pipe-go v0.2.1/go.mod h1:HNUWNjS7SjzhZ7McDLhvIBSOLz1X9DNLjj4J52pAj3Y=
 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
-github.com/xiaoqidun/entps v1.32.0 h1:shF2tBvry0F0wYlqeWpj7SUbq6fYS6cmHGDp5ZShKqg=
-github.com/xiaoqidun/entps v1.32.0/go.mod h1:42K0gnlDRuO4sc5GWmpFXz40kB81LjabFu/kK9BzKXI=
 golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
 golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
 golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
diff --git a/run.sh b/run.sh
index 64b35c5..fea0921 100755
--- a/run.sh
+++ b/run.sh
@@ -1,3 +1,3 @@
 set +e
 
-CGO_ENABLED=0 SESSION_KEY=aes1Itheich4aeQu9Ouz7ahcaiVoogh9 go run ./... -h ""
+CGO_ENABLED=0 SESSION_KEY=aes1Itheich4aeQu9Ouz7ahcaiVoogh9 go run ./src
diff --git a/src/battle.go b/src/battle.go
index e30cebd..01e9f8d 100644
--- a/src/battle.go
+++ b/src/battle.go
@@ -23,6 +23,7 @@ type Battle struct {
 	Archs     []Arch
 	Bits      []Bit
 	RawOutput string
+	MaxRounds int
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -32,8 +33,8 @@ func BattleGetAll() ([]Battle, error) {
 	return globalState.GetAllBattles()
 }
 
-func BattleCreate(name string, public bool) (int, error) {
-	return globalState.InsertBattle(Battle{Name: name, Public: public})
+func BattleCreate(name string, public bool, owner User) (int, error) {
+	return globalState.InsertBattle(Battle{Name: name, Public: public}, owner)
 }
 
 func BattleLinkBot(botid int, battleid int) error {
@@ -64,10 +65,15 @@ func BattleSaveRawOutput(battleid int, rawOutput string) error {
 	return globalState.UpdateBattleRawOutput(battleid, rawOutput)
 }
 
+func BattleDeleteID(battleid int) error {
+	return globalState.DeleteBattleByID(battleid)
+}
+
 //////////////////////////////////////////////////////////////////////////////
 // DATABASE
 
-func (s *State) InsertBattle(battle Battle) (int, error) {
+func (s *State) InsertBattle(battle Battle, owner User) (int, error) {
+	// create the battle
 	res, err := s.db.Exec("INSERT INTO battles VALUES(NULL,?,?,?);", time.Now(), battle.Name, battle.Public)
 	if err != nil {
 		log.Println(err)
@@ -79,6 +85,14 @@ func (s *State) InsertBattle(battle Battle) (int, error) {
 		log.Println(err)
 		return -1, err
 	}
+
+	// insert the owner into the battle_owner rel
+	_, err = s.db.Exec("INSERT INTO owner_battle_rel VALUES (?, ?)", owner.ID, battle.ID)
+	if err != nil {
+		log.Println(err)
+		return -1, err
+	}
+
 	return int(id), nil
 }
 
@@ -219,6 +233,7 @@ func (s *State) GetBattleByIdDeep(id int) (Battle, error) {
 	var battlename string
 	var battlepublic bool
 	var battlerawoutput string
+	var battlemaxrounds int
 
 	var botids string
 	var botnames string
@@ -239,10 +254,15 @@ func (s *State) GetBattleByIdDeep(id int) (Battle, error) {
 	// TODO(emile): go deeper! we could fetch battle -> bot -> arch (so fetching the linked arch
 	//              for the given bot)
 
+	// COALESCE is used to set default values
+	// TODO(emile): do fancy migrations instead of the COALESCE stuff for setting defaults if
+	// no value is set beforehand
+
 	err := s.db.QueryRow(`
 	SELECT DISTINCT
 		ba.id, ba.name, ba.public, 
 		COALESCE(ba.raw_output, ""),
+		COALESCE(ba.max_rounds, 100),
 		COALESCE(group_concat(DISTINCT bb.bot_id), ""),
 		COALESCE(group_concat(DISTINCT bo.name), ""),
 		COALESCE(group_concat(DISTINCT ub.user_id), ""),
@@ -267,7 +287,7 @@ func (s *State) GetBattleByIdDeep(id int) (Battle, error) {
 
 	WHERE ba.id=?
 	GROUP BY ba.id;
-	`, id).Scan(&battleid, &battlename, &battlepublic, &battlerawoutput, &botids, &botnames, &userids, &usernames, &archids, &archnames, &bitids, &bitnames)
+	`, id).Scan(&battleid, &battlename, &battlepublic, &battlerawoutput, &battlemaxrounds, &botids, &botnames, &userids, &usernames, &archids, &archnames, &bitids, &bitnames)
 	if err != nil {
 		log.Println(err)
 		return Battle{}, err
@@ -361,6 +381,7 @@ func (s *State) GetBattleByIdDeep(id int) (Battle, error) {
 		Archs:     archs,
 		Bits:      bits,
 		RawOutput: battlerawoutput,
+		MaxRounds: battlemaxrounds,
 	}, nil
 }
 
@@ -373,6 +394,35 @@ func (s *State) UpdateBattleRawOutput(battleid int, rawOutput string) error {
 	return nil
 }
 
+// This deletes a battle and all links to users, bots, architectures and bits
+func (s *State) DeleteBattleByID(battleid int) error {
+	_, err := s.db.Exec(`
+	DELETE FROM battles WHERE battleid = ?;
+
+	DELETE FROM user_battle_rel WHERE battleid = ?;
+	DELETE FROM bot_battle_rel WHERE battleid = ?;
+	DELETE FROM arch_battle_rel WHERE battleid = ?;
+	DELETE FROM bit_battle_rel WHERE battleid = ?;
+	`, battleid, battleid, battleid, battleid, battleid)
+
+	if err != nil {
+		log.Println(err)
+		return err
+	}
+
+	_, err = s.db.Exec(`
+	DELETE FROM battles
+	WHERE battleid = ?
+	`, battleid)
+
+	if err != nil {
+		log.Println(err)
+		return err
+	}
+
+	return nil
+}
+
 //////////////////////////////////////////////////////////////////////////////
 // HTTP
 
@@ -498,6 +548,20 @@ func battleNewHandler(w http.ResponseWriter, r *http.Request) {
 		t.ExecuteTemplate(w, "battleNew", data)
 
 	case "POST":
+		data := map[string]interface{}{}
+
+		session, _ := globalState.sessions.Get(r, "session")
+		username := session.Values["username"].(string)
+
+		// get data needed
+		user, err := UserGetUserFromUsername(username)
+		if err != nil {
+			log.Println(err)
+			data["err"] = "Could not fetch the user"
+		} else {
+			data["user"] = user
+		}
+
 		// parse the post parameters
 		r.ParseForm()
 		name := r.Form.Get("name")
@@ -538,7 +602,7 @@ func battleNewHandler(w http.ResponseWriter, r *http.Request) {
 		if name != "" {
 			// create the battle itself
 			log.Println("Creating battle")
-			battleid, err := BattleCreate(name, public)
+			battleid, err := BattleCreate(name, public, user)
 			if err != nil {
 				log.Println(err)
 				msg := "ERROR: Could not create due to internal reasons"
@@ -631,6 +695,20 @@ func battleQuickHandler(w http.ResponseWriter, r *http.Request) {
 		t.ExecuteTemplate(w, "battleQuick", data)
 
 	case "POST":
+		data := map[string]interface{}{}
+
+		session, _ := globalState.sessions.Get(r, "session")
+		username := session.Values["username"].(string)
+
+		// get data needed
+		user, err := UserGetUserFromUsername(username)
+		if err != nil {
+			log.Println(err)
+			data["err"] = "Could not fetch the user"
+		} else {
+			data["user"] = user
+		}
+
 		// parse the post parameters
 		r.ParseForm()
 
@@ -658,7 +736,7 @@ func battleQuickHandler(w http.ResponseWriter, r *http.Request) {
 
 		// create the battle itself
 		log.Println("Creating battle")
-		battleid, err := BattleCreate("quick", public)
+		battleid, err := BattleCreate("quick", public, user)
 		if err != nil {
 			log.Println(err)
 			msg := "ERROR: Could not create due to internal reasons"
@@ -872,6 +950,20 @@ func battleSingleHandler(w http.ResponseWriter, r *http.Request) {
 
 		// at this point, we're sure the user is allowed to edit the battle
 
+		data := map[string]interface{}{}
+
+		session, _ := globalState.sessions.Get(r, "session")
+		username := session.Values["username"].(string)
+
+		// get data needed
+		user, err := UserGetUserFromUsername(username)
+		if err != nil {
+			log.Println(err)
+			data["err"] = "Could not fetch the user"
+		} else {
+			data["user"] = user
+		}
+
 		r.ParseForm()
 
 		log.Println("r.Form: ", r.Form)
@@ -919,7 +1011,7 @@ func battleSingleHandler(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 
-		new_battle := Battle{int(battleid), form_name, []Bot{}, []User{}, public, []Arch{}, []Bit{}, ""}
+		new_battle := Battle{int(battleid), form_name, []Bot{}, []User{user}, public, []Arch{}, []Bit{}, "", 100}
 
 		log.Println("Updating battle...")
 		err = BattleUpdate(new_battle)
@@ -1220,12 +1312,65 @@ func battleRunHandler(w http.ResponseWriter, r *http.Request) {
 
 // delete a battle
 // TODO(emile): finish implementing the deletion of battles
-//  func battleDeleteHandler(w http.ResponseWriter, r *http.Request) {
-//  	switch r.Method {
-//  	case "DELETE":
-
-//  		http.Redirect(w, r, fmt.Sprintf("/battle?res=%s", battleid, msg), http.StatusSeeOther)
-//  	default:
-//  		http.Redirect(w, r, "/", http.StatusMethodNotAllowed)
-//  	}
-//  }
+func battleDeleteHandler(w http.ResponseWriter, r *http.Request) {
+	vars := mux.Vars(r)
+	battleid, err := strconv.Atoi(vars["id"])
+	if err != nil {
+		w.WriteHeader(http.StatusInternalServerError)
+		w.Write([]byte("500 - Invalid battle id"))
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+
+	redir_target := fmt.Sprintf("/battle/%d?res=%%s", battleid)
+
+	switch r.Method {
+	case "POST": // can't send a DELETE with pure HTML...
+
+		// get the current user
+		session, _ := globalState.sessions.Get(r, "session")
+		username := session.Values["username"]
+		if username == nil {
+			http.Redirect(w, r, "/login", http.StatusSeeOther)
+			return
+		}
+		viewer, err := UserGetUserFromUsername(username.(string))
+		if err != nil {
+			log_and_redir_with_msg(w, r, err, redir_target, "Could not get the id for your username")
+			return
+		}
+
+		// get the battle
+		battle, err := BattleGetByIdDeep(int(battleid))
+		if err != nil {
+			log_and_redir_with_msg(w, r, err, redir_target, "Could not get the battle given the id provided")
+			return
+		}
+
+		battle_owned_by_requesting_user := false
+		for _, owner := range battle.Owners {
+
+			// if the requesting users id is equal to an owners id, we're allowed to delete
+			// the battle
+			if viewer.ID == owner.ID {
+				battle_owned_by_requesting_user = true
+				break
+			}
+		}
+
+		// check that the user that created the battle is the current user, if not, return
+		if battle_owned_by_requesting_user == false {
+			msg := "You aren't in the owners list of the battle, so you can't delete this battle"
+			log_and_redir_with_msg(w, r, err, redir_target, msg)
+			return
+		}
+
+		BattleDeleteID(battleid)
+
+		msg := "Successfully deleted the battle"
+		http.Redirect(w, r, fmt.Sprintf("/battle/%d?res=%s", battleid, msg), http.StatusSeeOther)
+	default:
+		log.Println("expected POST, got ", r.Method)
+		http.Redirect(w, r, "/", http.StatusMethodNotAllowed)
+	}
+}
diff --git a/src/db.go b/src/db.go
index 426ac52..42dc2d4 100644
--- a/src/db.go
+++ b/src/db.go
@@ -24,6 +24,7 @@ CREATE TABLE IF NOT EXISTS battles (
 	name TEXT,
 	public BOOLEAN,
 	raw_output TEXT
+	max_rounds INTEGER
 );
 CREATE TABLE IF NOT EXISTS archs (
 	id INTEGER NOT NULL PRIMARY KEY,
@@ -90,6 +91,11 @@ CREATE TABLE IF NOT EXISTS user_battle_rel (
 	battle_id INTEGER,
 	PRIMARY KEY(user_id, battle_id)
 );
+CREATE TABLE IF NOT EXISTS owner_battle_rel (
+	user_id INTEGER,
+	battle_id INTEGER,
+	PRIMARY KEY(user_id, battle_id)
+);
 CREATE TABLE IF NOT EXISTS bot_battle_rel (
 	bot_id INTEGER,
 	battle_id INTEGER,
diff --git a/src/main.go b/src/main.go
index 20c245a..c789cf8 100644
--- a/src/main.go
+++ b/src/main.go
@@ -30,7 +30,7 @@ func initFlags() {
 
 	flag.StringVar(&logFilePath, "logfilepath", "./server.log", "The path to the log file")
 	flag.StringVar(&databasePath, "databasepath", "./main.db", "The path to the main database")
-	flag.StringVar(&sessiondbPath, "sessiondbpath", "./sesions.db", "The path to the session database")
+	flag.StringVar(&sessiondbPath, "sessiondbpath", "./sessions.db", "The path to the session database")
 	flag.StringVar(&templatesPath, "templates", "./templates", "The path to the templates used")
 }
 
@@ -92,7 +92,7 @@ func main() {
 	auth_needed.HandleFunc("/battle/quick", battleQuickHandler)
 	auth_needed.HandleFunc("/battle/{id}/submit", battleSubmitHandler)
 	auth_needed.HandleFunc("/battle/{id}/run", battleRunHandler)
-	//  auth_needed.HandleFunc("/battle/{id}/delete", battleDeleteHandler)
+	auth_needed.HandleFunc("/battle/{id}/delete", battleDeleteHandler)
 
 	log.Printf("[i] HTTP Server running on %s:%d\n", host, port)
 	log.Fatal(http.ListenAndServe(fmt.Sprintf("%s:%d", host, port), r))
diff --git a/src/user.go b/src/user.go
index d55c9d8..1fd9358 100644
--- a/src/user.go
+++ b/src/user.go
@@ -119,7 +119,9 @@ func (s *State) UpdateUserUsername(id int, new_username string) error {
 
 // Links the given bot to the given user in the user_bot_rel table
 func (s *State) LinkUserBot(username string, botid int) error {
-	_, err := s.db.Exec("INSERT INTO user_bot_rel VALUES ((SELECT id FROM users WHERE name=?), ?)", username, botid)
+	_, err := s.db.Exec(`
+		INSERT INTO user_bot_rel
+		VALUES ((SELECT id FROM users WHERE name=?), ?)`, username, botid)
 	if err != nil {
 		return err
 	} else {
@@ -153,7 +155,11 @@ func (s *State) GetUserFromUsername(username string) (User, error) {
 // Returns the bots belonging to the given user
 // TODO(emile): Also fetch the bits and the archs for displaying in the single battle page. In order to do so, join in both those tables
 func (s *State) GetUserBotsUsername(username string) ([]Bot, error) {
-	rows, err := s.db.Query("SELECT id, name, source FROM bots b LEFT JOIN user_bot_rel ub ON ub.bot_id = b.id WHERE ub.user_id=(SELECT id FROM users WHERE name=?)", username)
+	rows, err := s.db.Query(`
+		SELECT id, name, source
+		FROM bots b
+		LEFT JOIN user_bot_rel ub ON ub.bot_id = b.id
+		WHERE ub.user_id=(SELECT id FROM users WHERE name=?)`, username)
 	defer rows.Close()
 	if err != nil {
 		return nil, err
@@ -175,7 +181,11 @@ func (s *State) GetUserBotsUsername(username string) ([]Bot, error) {
 
 // Returns the bots belonging to the given user
 func (s *State) GetUserBotsId(id int) ([]Bot, error) {
-	rows, err := s.db.Query("SELECT id, name, source FROM bots b LEFT JOIN user_bot_rel ub ON ub.bot_id = b.id WHERE ub.user_id=?", id)
+	rows, err := s.db.Query(`
+		SELECT id, name, source
+		FROM bots b
+		LEFT JOIN user_bot_rel ub ON ub.bot_id = b.id
+		WHERE ub.user_id=?`, id)
 	defer rows.Close()
 	if err != nil {
 		return nil, err
diff --git a/templates/battleSingle.html b/templates/battleSingle.html
index b5bb6d6..267a0df 100644
--- a/templates/battleSingle.html
+++ b/templates/battleSingle.html
@@ -11,116 +11,115 @@
 <a href="#settings">Settings</a>
 <a href="#registered-bots">Registered Bots</a>
 <a href="#output">Output</a>
+<a href="#debug">Debug</a>
   </pre>
 
   <span id="settings"></span>
   <h2><a href="#settings">Settings</a></h2>
 
-  <form id="delete" method="DELETE" action="/battle/{{ .battle.ID }}/delete">
-  </form>
-
   <table>
-    <form id="battle" method="POST" action="/battle/{{ .battle.ID }}">
-      <tr>
-        <td><label for="name">Name:</label></td>
-        <td><input class="border" type="text" id="name" name="name" value="{{ .battle.Name }}"></td>
-      </tr>
-
-      <!--
-      <tr>
-        <td><label for="latestBotSubmission">Latest Bot Submission</label></td>
-        <td><input
-              class="border"
-              type="datetime-local"
-              id="latestBotSubmission"
-              name="latestBotSubmission"
-              value="2024-11-08T12:00"
-              ></td>
-      </tr>
-
-      <tr>
-        <td><label for="battleStart">Battle Start</label></td>
-        <td><input
-              class="border"
-              type="datetime-local"
-              id="battleStart"
-              name="battleStart"
-              value="2024-11-08T16:00"
-              ></td>
-      </tr>
-
-      <tr>
-        <td><label for="owners">Owners:</label></td>
-        <td>
-          {{ range $idx, $usr := .battle.Owners }}{{if $idx}},{{end}}<a href="/user/{{ $usr.ID }}">{{ $usr.Name }}</a>{{ end }}
-        </td>
-      </tr>
-      -->
-
-      <tr>
-        <td><label for="bots">Bots submitted:</label></td>
-        <td>
-          {{ range $idx, $bot := .battle.Bots }}{{if $idx}},{{end}}<a href="/bot/{{ $bot.ID }}">{{ $bot.Name }}</a>{{ end }}
+    <tbody>
+      <form id="battle" method="POST" action="/battle/{{ .battle.ID }}">
+        <tr>
+          <td><label for="name">Name:</label></td>
+          <td><input class="border" type="text" id="name" name="name" value="{{ .battle.Name }}"></td>
+        </tr>
+
+        <!--
+        <tr>
+          <td><label for="latestBotSubmission">Latest Bot Submission</label></td>
+          <td><input
+                class="border"
+                type="datetime-local"
+                id="latestBotSubmission"
+                name="latestBotSubmission"
+                value="2024-11-08T12:00"
+                ></td>
+        </tr>
+
+        <tr>
+          <td><label for="battleStart">Battle Start</label></td>
+          <td><input
+                class="border"
+                type="datetime-local"
+                id="battleStart"
+                name="battleStart"
+                value="2024-11-08T16:00"
+                ></td>
+        </tr>
+
+        <tr>
+          <td><label for="owners">Owners:</label></td>
+          <td>
+            {{ range $idx, $usr := .battle.Owners }}{{if $idx}},{{end}}<a href="/user/{{ $usr.ID }}">{{ $usr.Name }}</a>{{ end }}
+          </td>
+        </tr>
+        -->
+
+        <tr>
+          <td><label for="bots">Bots submitted:</label></td>
+          <td>
+            {{ range $idx, $bot := .battle.Bots }}{{if $idx}},{{end}}<a href="/bot/{{ $bot.ID }}">{{ $bot.Name }}</a>{{ end }}
+          </td>
+        </tr>
+
+        <!--
+        <tr>
+          <td><label for="public">Public?</label></td>
+          <td><input type="checkbox" id="public" name="public" {{ if .battle.Public }}checked{{end}}/></td>
+        </tr>
+        -->
+
+        <tr>
+          <td>Archs</td>
+          <td>
+            {{ range $idx, $arch := .archs }}{{if $idx}},{{end}}
+              <input
+                type="checkbox"
+                class="check-with-label"
+                name="arch-{{$arch.ID}}"
+                id="arch-{{$arch.ID}}"
+                {{if $arch.Enabled}}checked{{end}}/>
+              <label class="label-for-check" for="arch-{{$arch.ID}}">{{$arch.Name}}</label>
+            {{- end }}
+          </td>
         </td>
-      </tr>
 
-      <!--
-      <tr>
-        <td><label for="public">Public?</label></td>
-        <td><input type="checkbox" id="public" name="public" {{ if .battle.Public }}checked{{end}}/></td>
-      </tr>
-      -->
-
-      <tr>
-        <td>Archs</td>
-        <td>
-          {{ range $idx, $arch := .archs }}{{if $idx}},{{end}}
+        <tr>
+          <td>Bits</td>
+          <td>{{ range $idx, $bit := .bits }}{{if $idx}},{{end}}
             <input
               type="checkbox"
               class="check-with-label"
-              name="arch-{{$arch.ID}}"
-              id="arch-{{$arch.ID}}"
-              {{if $arch.Enabled}}checked{{end}}/>
-            <label class="label-for-check" for="arch-{{$arch.ID}}">{{$arch.Name}}</label>
-          {{- end }}
-        </td>
-      </td>
+              id="bit-{{$bit.ID}}"
+              name="bit-{{$bit.ID}}"
+              {{if $bit.Enabled}}checked{{end}}/>
+            <label class="label-for-check" for="bit-{{$bit.ID}}">{{$bit.Name}}</label>
+            {{- end }}
+          </td>
+        </tr>
+
+        <tr>
+          <td></td>
+          <td>
+            <table>
+              <tr>
+                <td style="width: 33%;"><input class="border" type="submit" value="Save"></td>
+                <td style="width: 33%;"></td>
+                <td style="width: 33%; ">
+                  <!--<input type="submit" value="Delete this battle" form="delete" style="border: 1px solid red; ">-->
+                </td>
+              </tr>
+            </table>
+          </td>
+        </tr>
+      </form>
 
-      <tr>
-        <td>Bits</td>
-        <td>{{ range $idx, $bit := .bits }}{{if $idx}},{{end}}
-          <input
-            type="checkbox"
-            class="check-with-label"
-            id="bit-{{$bit.ID}}"
-            name="bit-{{$bit.ID}}"
-            {{if $bit.Enabled}}checked{{end}}/>
-          <label class="label-for-check" for="bit-{{$bit.ID}}">{{$bit.Name}}</label>
-          {{- end }}
-        </td>
-      </td>
-
-      <tr>
-        <td></td>
-        <td>
-          <table>
-            <tr>
-              <td style="width: 33%;"><input class="border" type="submit" value="Save"></td>
-              <td style="width: 33%;"></td>
-              <td style="width: 33%; ">
-                <input type="submit" value="Delete this battle" form="delete" style="border: 1px solid red; ">
-              </td>
-            </tr>
-          </table>
-        </td>
-      </tr>
-
-    </form>
 
     {{ if .res }}
     <tr>
       <td></td>
-      <td>{{ .res }}</td>
+      <td><div style="border: 1px solid blue; padding: 1ex">{{ .res }}</div></td>
     </tr>
     {{ end }}
 
@@ -131,41 +130,38 @@
 
     {{ if .myBots }}
 
-          <form method="POST" action="/battle/{{ .battle.ID }}/submit">
-    <tr>
-      <td><label for="name">My Bots</label></td>
-      <td style="width: 100%;">
-        <table style="width: 100%;">
-
-            {{ range $bot := .myBots  }}
-            <tr class="trhover">
-              <td style="text-align: center; vertical-align: middle; width: 2ex;">
-                <input
-                  type="checkbox"
-                  id="bot-{{$bot.ID}}"
-                  name="bot-{{$bot.ID}}"
-                  value="{{$bot.ID}}"
-                  {{ range $bbot := $.battle.Bots }}
-                  {{ if eq $bot.ID $bbot.ID }}checked{{ end }}
-                  {{ end }}
-                  />
-                <label for="bot-{{$bot.ID}}">
-                  <a href="/bot/{{$bot.ID}}">{{$bot.Name}}</a>
-                </label>
-              </td>
-              <td style="vertical-align: middle">
-              </td>
-            </tr>
-            {{ end }}
-
-          
-        </table>
-      </td>
-    </tr>
-            <tr>
-              <td colspan="5"><input class="border" type="submit" value="Submit bots"></td>
-            </tr>
-          </form>
+    <form method="POST" action="/battle/{{ .battle.ID }}/submit">
+      <tr>
+        <td><label for="name">My Bots</label></td>
+        <td style="width: 100%;">
+          <table style="width: 100%;">
+              {{ range $bot := .myBots  }}
+              <tr class="trhover">
+                <td style="text-align: center; vertical-align: middle; width: 2ex;">
+                  <input
+                    type="checkbox"
+                    id="bot-{{$bot.ID}}"
+                    name="bot-{{$bot.ID}}"
+                    value="{{$bot.ID}}"
+                    {{ range $bbot := $.battle.Bots }}
+                    {{ if eq $bot.ID $bbot.ID }}checked{{ end }}
+                    {{ end }}
+                    />
+                  <label for="bot-{{$bot.ID}}">
+                    <a href="/bot/{{$bot.ID}}">{{$bot.Name}}</a>
+                  </label>
+                </td>
+                <td style="vertical-align: middle">
+                </td>
+              </tr>
+              {{ end }}
+          </table>
+        </td>
+      </tr>
+      <tr>
+        <td colspan="5"><input class="border" type="submit" value="Submit bots"></td>
+      </tr>
+    </form>
 
     {{ else }}
 
@@ -176,6 +172,7 @@
 
     {{ end }}
 
+    </tbody>
   <table>
 
   <br>
@@ -184,6 +181,12 @@
     <input class="border" type="submit" value="Run the Battle">
   </form>
 
+  <br><br>
+
+  <form id="delete" method="POST" action="/battle/{{ .battle.ID }}/delete">
+    <input type="submit" value="Delete this battle" form="delete" style="border: 1px solid red; ">
+  </form>
+
   <span id="registered bots"></span>
   <h2><a href="#registered-bots">Registered Bots</a></h2>
 
@@ -194,7 +197,10 @@
 
   <pre>{{ .battle.RawOutput }}</pre>
 
-  <span id="current-standings"></span>
+  <span id="debug"></span>
+  <h2><a href="#debug">Debug</a></h2>
+
+  <pre>{{ .battle }}</pre>
 </body>
 {{ template "footer" . }}
 {{ end }}
diff --git a/templates/nav.html b/templates/nav.html
index 117f48a..cd710cb 100644
--- a/templates/nav.html
+++ b/templates/nav.html
@@ -1,7 +1,7 @@
 {{ define "nav" }}
   <header>
-    <p style="margin: 1ex 0; display: block; width: 100%; background-color: red; color: white;">
-      EARLY BETA - Data can be deleted at random!
+    <p style="margin: 1ex 0; display: block; width: 100%; background-color: #ffaa00; color: white;">
+      EARLY BETA - Data can be deleted at random! - insecure af but almost functional
     </p>
 
     <a href="/">r2wa.rs</a>
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 7f035d7..e5c09e7 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -36,8 +36,6 @@ github.com/radareorg/r2pipe-go
 # github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec
 ## explicit; go 1.12
 github.com/remyoudompheng/bigfft
-# github.com/xiaoqidun/entps v1.32.0
-## explicit; go 1.20
 # golang.org/x/crypto v0.26.0
 ## explicit; go 1.20
 golang.org/x/crypto/argon2