about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEmile <git@emile.space>2024-11-09 16:28:47 +0100
committerEmile <git@emile.space>2024-11-09 16:28:47 +0100
commit5fb4b1f7dd6b373786a64dfe0ac59955fb6d964c (patch)
tree26ae664159632ae14b2c608d01ee4561b931bf8d
parentc685cc8f25adc5b2b72e4bda185fef7ec8dd6592 (diff)
yolo r2con push all
-rw-r--r--README.md8
-rw-r--r--src/battle.go417
-rw-r--r--src/bot.go16
-rw-r--r--src/db.go5
-rw-r--r--src/user.go34
-rw-r--r--templates/battleNew.html40
-rw-r--r--templates/battleSingle.html177
7 files changed, 545 insertions, 152 deletions
diff --git a/README.md b/README.md
index 8d996a1..558db4f 100644
--- a/README.md
+++ b/README.md
@@ -63,11 +63,11 @@ There are essentially the following objects which are all linked to each other (
 
 - [ ] 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
+- [x] Implement submitting bots
+- [x] Implement running the battle
+- [x] 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
+- [x] 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/src/battle.go b/src/battle.go
index 01e9f8d..3f82cf7 100644
--- a/src/battle.go
+++ b/src/battle.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"html/template"
 	"log"
+	"math/rand"
 	"net/http"
 	"os"
 	"strconv"
@@ -24,6 +25,7 @@ type Battle struct {
 	Bits      []Bit
 	RawOutput string
 	MaxRounds int
+	ArenaSize int
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -33,8 +35,8 @@ func BattleGetAll() ([]Battle, error) {
 	return globalState.GetAllBattles()
 }
 
-func BattleCreate(name string, public bool, owner User) (int, error) {
-	return globalState.InsertBattle(Battle{Name: name, Public: public}, owner)
+func BattleCreate(battle Battle, owner User) (int, error) {
+	return globalState.InsertBattle(battle, owner)
 }
 
 func BattleLinkBot(botid int, battleid int) error {
@@ -61,6 +63,10 @@ func BattleLinkBitIDs(battleid int, bitIDs []int) error {
 	return globalState.LinkBitIDsToBattle(battleid, bitIDs)
 }
 
+func BattleLinkOwnerIDs(battleid int, ownerIDs []int) error {
+	return globalState.LinkOwnerIDsToBattle(battleid, ownerIDs)
+}
+
 func BattleSaveRawOutput(battleid int, rawOutput string) error {
 	return globalState.UpdateBattleRawOutput(battleid, rawOutput)
 }
@@ -74,7 +80,16 @@ func BattleDeleteID(battleid 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)
+	res, err := s.db.Exec(`
+		INSERT INTO battles
+		VALUES(NULL,?,?,?,?,?,?)
+		`, time.Now(),
+		battle.Name,
+		battle.Public,
+		battle.RawOutput,
+		battle.MaxRounds,
+		battle.ArenaSize)
+
 	if err != nil {
 		log.Println(err)
 		return -1, err
@@ -97,11 +112,22 @@ func (s *State) InsertBattle(battle Battle, owner User) (int, error) {
 }
 
 func (s *State) UpdateBattle(battle Battle) error {
-	_, err := s.db.Exec("UPDATE battles SET name=?, public=? WHERE id=?", battle.Name, battle.Public, battle.ID)
+	log.Println("Updating battle:")
+	log.Println(battle.ArenaSize)
+	_, err := s.db.Exec(`
+		UPDATE battles
+		SET name=?, public=?, arena_size=?, max_rounds=?
+		WHERE id=?`,
+		battle.Name,
+		battle.Public,
+		battle.ArenaSize,
+		battle.MaxRounds,
+		battle.ID)
 	if err != nil {
 		log.Println(err)
 		return err
 	}
+	log.Println("Done updating")
 	return nil
 }
 
@@ -115,6 +141,16 @@ func (s *State) LinkBotBattle(botid int, battleid int) error {
 	}
 }
 
+func (s *State) LinkOwnerBattle(battleid int, ownerid int) error {
+	_, err := s.db.Exec("INSERT INTO owner_battle_rel VALUES (?, ?)", ownerid, battleid)
+	if err != nil {
+		log.Println(err)
+		return err
+	} else {
+		return nil
+	}
+}
+
 func (s *State) UnlinkAllBotsForUserFromBattle(userid int, battleid int) error {
 	// get a user with the given id
 	// for all of their bots
@@ -148,7 +184,7 @@ func (s *State) UnlinkAllBotsForUserFromBattle(userid int, battleid int) error {
 
 func (s *State) LinkArchIDsToBattle(battleid int, archIDs []int) error {
 	// delete preexisting links
-	_, err := s.db.Exec("DELETE FROM arch_battle_rel WHERE battle_id=?;", battleid)
+	_, err := s.db.Exec("DELETE FROM arch_battle_rel WHERE battle_id=?", battleid)
 	if err != nil {
 		log.Println(err)
 		return err
@@ -177,7 +213,7 @@ func (s *State) LinkArchIDsToBattle(battleid int, archIDs []int) error {
 
 func (s *State) LinkBitIDsToBattle(battleid int, bitIDs []int) error {
 	// delete preexisting links
-	_, err := s.db.Exec("DELETE FROM bit_battle_rel WHERE battle_id=?;", battleid)
+	_, err := s.db.Exec("DELETE FROM bit_battle_rel WHERE battle_id=?", battleid)
 	if err != nil {
 		log.Println(err)
 		return err
@@ -204,6 +240,35 @@ func (s *State) LinkBitIDsToBattle(battleid int, bitIDs []int) error {
 	}
 }
 
+func (s *State) LinkOwnerIDsToBattle(battleid int, ownerIDs []int) error {
+	// delete preexisting links
+	_, err := s.db.Exec("DELETE FROM owner_battle_rel WHERE battle_id=?", battleid)
+	if err != nil {
+		log.Println(err)
+		return err
+	}
+
+	// yes, we're building this by hand, but as we only insert int's I'm just confident that whoever
+	// gets some sqli here just deserves it :D
+	query := "INSERT INTO owner_battle_rel (user_id, battle_id) VALUES"
+	for idx, id := range ownerIDs {
+		query += fmt.Sprintf("(%d, %d)", id, battleid)
+		if idx != len(ownerIDs)-1 {
+			query += ", "
+		}
+	}
+	query += ";"
+	log.Println(query)
+
+	_, err = s.db.Exec(query)
+	if err != nil {
+		log.Println(err)
+		return err
+	} else {
+		return nil
+	}
+}
+
 func (s *State) GetAllBattles() ([]Battle, error) {
 	rows, err := s.db.Query("SELECT id, name FROM battles;")
 	defer rows.Close()
@@ -234,6 +299,7 @@ func (s *State) GetBattleByIdDeep(id int) (Battle, error) {
 	var battlepublic bool
 	var battlerawoutput string
 	var battlemaxrounds int
+	var battlearenasize int
 
 	var botids string
 	var botnames string
@@ -247,6 +313,9 @@ func (s *State) GetBattleByIdDeep(id int) (Battle, error) {
 	var bitids string
 	var bitnames string
 
+	var ownerids string
+	var ownernames string
+
 	// battles have associated bots and users, we're fetching 'em all!
 
 	// This fetches the battles and relates the associated bots, users, archs and bits
@@ -263,14 +332,22 @@ func (s *State) GetBattleByIdDeep(id int) (Battle, error) {
 		ba.id, ba.name, ba.public, 
 		COALESCE(ba.raw_output, ""),
 		COALESCE(ba.max_rounds, 100),
+		COALESCE(ba.arena_size, 4096),
+
 		COALESCE(group_concat(DISTINCT bb.bot_id), ""),
 		COALESCE(group_concat(DISTINCT bo.name), ""),
+
 		COALESCE(group_concat(DISTINCT ub.user_id), ""),
 		COALESCE(group_concat(DISTINCT us.name), ""),
+
 		COALESCE(group_concat(DISTINCT ab.arch_id), ""),
 		COALESCE(group_concat(DISTINCT ar.name), ""),
+
 		COALESCE(group_concat(DISTINCT bitbat.bit_id), ""),
-		COALESCE(group_concat(DISTINCT bi.name), "")
+		COALESCE(group_concat(DISTINCT bi.name), ""),
+
+		COALESCE(group_concat(DISTINCT ownerbat.user_id), ""),
+		COALESCE(group_concat(DISTINCT owner.name), "")
 	FROM battles ba
 
 	LEFT JOIN bot_battle_rel bb ON bb.battle_id = ba.id
@@ -285,9 +362,12 @@ func (s *State) GetBattleByIdDeep(id int) (Battle, error) {
 	LEFT JOIN bit_battle_rel bitbat ON bitbat.battle_id = ba.id
 	LEFT JOIN bits bi ON bi.id = bitbat.bit_id
 
+	LEFT JOIN owner_battle_rel ownerbat ON ownerbat.battle_id = ba.id
+	LEFT JOIN users owner ON owner.id = ownerbat.user_id
+
 	WHERE ba.id=?
 	GROUP BY ba.id;
-	`, id).Scan(&battleid, &battlename, &battlepublic, &battlerawoutput, &battlemaxrounds, &botids, &botnames, &userids, &usernames, &archids, &archnames, &bitids, &bitnames)
+	`, id).Scan(&battleid, &battlename, &battlepublic, &battlerawoutput, &battlemaxrounds, &battlearenasize, &botids, &botnames, &userids, &usernames, &archids, &archnames, &bitids, &bitnames, &ownerids, &ownernames)
 	if err != nil {
 		log.Println(err)
 		return Battle{}, err
@@ -372,16 +452,35 @@ func (s *State) GetBattleByIdDeep(id int) (Battle, error) {
 		bits = []Bit{}
 	}
 
+	// assemble the owners
+	ownerIDList := strings.Split(ownerids, ",")
+	ownerNameList := strings.Split(ownernames, ",")
+
+	var owners []User
+	if ownerIDList[0] != "" {
+		for i := range ownerIDList {
+			id, err := strconv.Atoi(ownerIDList[i])
+			if err != nil {
+				log.Println(err)
+				return Battle{}, err
+			}
+			owners = append(owners, User{id, ownerNameList[i], nil})
+		}
+	} else {
+		owners = []User{}
+	}
+
 	return Battle{
 		ID:        battleid,
 		Name:      battlename,
 		Bots:      bots,
-		Owners:    users,
+		Owners:    owners,
 		Public:    battlepublic,
 		Archs:     archs,
 		Bits:      bits,
 		RawOutput: battlerawoutput,
 		MaxRounds: battlemaxrounds,
+		ArenaSize: battlearenasize,
 	}, nil
 }
 
@@ -534,6 +633,14 @@ func battleNewHandler(w http.ResponseWriter, r *http.Request) {
 			data["bits"] = bits
 		}
 
+		users, err := UserGetAll()
+		if err != nil {
+			log.Println(err)
+			data["err"] = "Could not fetch all users"
+		} else {
+			data["users"] = users
+		}
+
 		// get the template
 		t, err := template.ParseGlob(fmt.Sprintf("%s/*.html", templatesPath))
 		if err != nil {
@@ -545,7 +652,10 @@ func battleNewHandler(w http.ResponseWriter, r *http.Request) {
 		}
 
 		// exec!
-		t.ExecuteTemplate(w, "battleNew", data)
+		err = t.ExecuteTemplate(w, "battleNew", data)
+		if err != nil {
+			log.Println(err)
+		}
 
 	case "POST":
 		data := map[string]interface{}{}
@@ -565,6 +675,24 @@ func battleNewHandler(w http.ResponseWriter, r *http.Request) {
 		// parse the post parameters
 		r.ParseForm()
 		name := r.Form.Get("name")
+		arenasize, err := strconv.Atoi(r.Form.Get("arena-size"))
+		if err != nil {
+			// TODO(emile): use the log_and_redir function in here (and the surrounding code)
+
+			log.Println(err)
+			msg := "ERROR: Invalid arch id"
+			http.Redirect(w, r, fmt.Sprintf("/battle/new?res=%s", msg), http.StatusSeeOther)
+			return
+		}
+		maxrounds, err := strconv.Atoi(r.Form.Get("max-rounds"))
+		if err != nil {
+			// TODO(emile): use the log_and_redir function in here (and the surrounding code)
+
+			log.Println(err)
+			msg := "ERROR: Invalid arch id"
+			http.Redirect(w, r, fmt.Sprintf("/battle/new?res=%s", msg), http.StatusSeeOther)
+			return
+		}
 
 		var public bool
 		query_public := r.Form.Get("public")
@@ -576,7 +704,7 @@ func battleNewHandler(w http.ResponseWriter, r *http.Request) {
 		var archIDs []int
 		var bitIDs []int
 
-		for k, _ := range r.Form {
+		for k := range r.Form {
 			if strings.HasPrefix(k, "arch-") {
 				id, err := strconv.Atoi(strings.TrimPrefix(k, "arch-"))
 				if err != nil {
@@ -602,7 +730,19 @@ func battleNewHandler(w http.ResponseWriter, r *http.Request) {
 		if name != "" {
 			// create the battle itself
 			log.Println("Creating battle")
-			battleid, err := BattleCreate(name, public, user)
+			newbattle := Battle{
+				0,
+				name,
+				nil,
+				nil,
+				public,
+				nil,
+				nil,
+				"",
+				maxrounds,
+				arenasize,
+			}
+			battleid, err := BattleCreate(newbattle, user)
 			if err != nil {
 				log.Println(err)
 				msg := "ERROR: Could not create due to internal reasons"
@@ -627,6 +767,15 @@ func battleNewHandler(w http.ResponseWriter, r *http.Request) {
 				http.Redirect(w, r, fmt.Sprintf("/battle/new?res=%s", msg), http.StatusSeeOther)
 				return
 			}
+
+			// link owner to battle
+			err = BattleLinkOwnerIDs(battleid, []int{user.ID})
+			if err != nil {
+				log.Println(err)
+				msg := "ERROR: Could not create due to internal reasons"
+				http.Redirect(w, r, fmt.Sprintf("/battle/new?res=%s", msg), http.StatusSeeOther)
+				return
+			}
 		} else {
 			msg := "ERROR: Please provide a name"
 			http.Redirect(w, r, fmt.Sprintf("/battle/new?res=%s", msg), http.StatusSeeOther)
@@ -718,10 +867,29 @@ func battleQuickHandler(w http.ResponseWriter, r *http.Request) {
 			public = true
 		}
 
+		arenasize, err := strconv.Atoi(r.Form.Get("arena-size"))
+		if err != nil {
+			// TODO(emile): use the log_and_redir function in here (and the surrounding code)
+
+			log.Println(err)
+			msg := "ERROR: Invalid arch id"
+			http.Redirect(w, r, fmt.Sprintf("/battle/new?res=%s", msg), http.StatusSeeOther)
+			return
+		}
+		maxrounds, err := strconv.Atoi(r.Form.Get("max-rounds"))
+		if err != nil {
+			// TODO(emile): use the log_and_redir function in here (and the surrounding code)
+
+			log.Println(err)
+			msg := "ERROR: Invalid arch id"
+			http.Redirect(w, r, fmt.Sprintf("/battle/new?res=%s", msg), http.StatusSeeOther)
+			return
+		}
+
 		// gather the information from the arch and bit selection
 		var botIDs []int
 
-		for k, _ := range r.Form {
+		for k := range r.Form {
 			if strings.HasPrefix(k, "bot-") {
 				id, err := strconv.Atoi(strings.TrimPrefix(k, "bot-"))
 				if err != nil {
@@ -736,7 +904,19 @@ func battleQuickHandler(w http.ResponseWriter, r *http.Request) {
 
 		// create the battle itself
 		log.Println("Creating battle")
-		battleid, err := BattleCreate("quick", public, user)
+		newbattle := Battle{
+			0,
+			fmt.Sprintf("quick-%d", rand.Intn(10000)),
+			nil,
+			nil,
+			public,
+			nil,
+			nil,
+			"",
+			maxrounds,
+			arenasize,
+		}
+		battleid, err := BattleCreate(newbattle, user)
 		if err != nil {
 			log.Println(err)
 			msg := "ERROR: Could not create due to internal reasons"
@@ -820,6 +1000,13 @@ func battleSingleHandler(w http.ResponseWriter, r *http.Request) {
 		}
 		data["user"] = viewer
 
+		users, err := UserGetAll()
+		if err != nil {
+			log_and_redir_with_msg(w, r, err, redir_target, "Could not get the users")
+			return
+		}
+		data["users"] = users
+
 		// get the battle including it's users, bots, archs, bits
 		battle, err := BattleGetByIdDeep(int(battleid))
 		if err != nil {
@@ -968,6 +1155,11 @@ func battleSingleHandler(w http.ResponseWriter, r *http.Request) {
 
 		log.Println("r.Form: ", r.Form)
 		form_name := r.Form.Get("name")
+		arenasize, err := strconv.Atoi(r.Form.Get("arena-size"))
+		if err != nil {
+			log_and_redir_with_msg(w, r, err, redir_target, "Invalid Arena size")
+			return
+		}
 
 		var public bool
 		if r.Form.Get("public") == "on" {
@@ -977,8 +1169,11 @@ func battleSingleHandler(w http.ResponseWriter, r *http.Request) {
 		// gather the information from the arch and bit selection
 		var archIDs []int
 		var bitIDs []int
+		var ownerIDs []int
+
+		log.Println(r.Form)
 
-		for k, _ := range r.Form {
+		for k := range r.Form {
 			if strings.HasPrefix(k, "arch-") {
 				id, err := strconv.Atoi(strings.TrimPrefix(k, "arch-"))
 				if err != nil {
@@ -995,8 +1190,30 @@ func battleSingleHandler(w http.ResponseWriter, r *http.Request) {
 				}
 				bitIDs = append(bitIDs, id)
 			}
+			if strings.HasPrefix(k, "owner-") {
+				id, err := strconv.Atoi(strings.TrimPrefix(k, "owner-"))
+				if err != nil {
+					log_and_redir_with_msg(w, r, err, redir_target, "Invalid Owner ID")
+					return
+				}
+				ownerIDs = append(ownerIDs, id)
+			}
 		}
 
+		// CHECK THAT THE USER REQUESTING CHANGES IS ALREADY PART OF THE OWNERS
+		allowedToEdit := false
+		for _, ownerID := range ownerIDs {
+			if user.ID == ownerID {
+				allowedToEdit = true
+			}
+		}
+		if allowedToEdit == false {
+			log_and_redir_with_msg(w, r, err, redir_target+"#settings", "You aren't an owner and aren't allowed to edit the settings")
+			return
+		}
+
+		// DATABASE MANIPUTLATION BELOW
+
 		// link archs to battle
 		err = BattleLinkArchIDs(battleid, archIDs)
 		if err != nil {
@@ -1011,7 +1228,14 @@ func battleSingleHandler(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 
-		new_battle := Battle{int(battleid), form_name, []Bot{}, []User{user}, public, []Arch{}, []Bit{}, "", 100}
+		// link bits to battle
+		err = BattleLinkOwnerIDs(battleid, ownerIDs)
+		if err != nil {
+			log_and_redir_with_msg(w, r, err, redir_target+"#settings", "Could not link owner id to battle")
+			return
+		}
+
+		new_battle := Battle{int(battleid), form_name, []Bot{}, []User{user}, public, []Arch{}, []Bit{}, "", 100, arenasize}
 
 		log.Println("Updating battle...")
 		err = BattleUpdate(new_battle)
@@ -1062,7 +1286,7 @@ func battleSubmitHandler(w http.ResponseWriter, r *http.Request) {
 
 		// get all the form values that contain the bot that shall be submitted
 		var botIDs []int
-		for k, _ := range r.Form {
+		for k := range r.Form {
 			if strings.HasPrefix(k, "bot-") {
 				id, err := strconv.Atoi(strings.TrimPrefix(k, "bot-"))
 				if err != nil {
@@ -1170,31 +1394,58 @@ func battleRunHandler(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 
+		// Fetch the battle information
+		// This includes all bots linked to the battle
+		log.Printf("user %+v wants to run the battle", user)
+		fullDeepBattle, err := BattleGetByIdDeep(battleid)
+
 		// open radare without input for building the bot
 		// TODO(emile): configure a variable memsize for the arena
-		r2p1, err := r2pipe.NewPipe("malloc://4096")
+		cmd := fmt.Sprintf("malloc://%d", fullDeepBattle.ArenaSize)
+		r2p1, err := r2pipe.NewPipe(cmd)
 		if err != nil {
 			panic(err)
 		}
 		defer r2p1.Close()
 
-		// Fetch the battle information
-		// This includes all bots linked to the battle
-		log.Printf("user %+v wants to run the battle", user)
-		fullDeepBattle, err := BattleGetByIdDeep(battleid)
-
 		var botSources []string
 		var rawOutput string
 
+		cmd = fmt.Sprintf("pxc %d @ 0x0", fullDeepBattle.ArenaSize)
+		output, _ := r2cmd(r2p1, cmd)
+		rawOutput += fmt.Sprintf("[0x00000000]> %s\n%s\n", cmd, output)
+
+		// TODO(emile): currently hardcoded to two bots, extract this anonymous struct into a named struct and make this work with > 2 bots
+		runtimeBots := []struct {
+			Name     string
+			Regs     string
+			BaseAddr int
+			ArchName string
+			BitsName string
+		}{
+			{Name: "", Regs: "", BaseAddr: 0, ArchName: "", BitsName: ""},
+			{Name: "", Regs: "", BaseAddr: 0, ArchName: "", BitsName: ""},
+		}
+
+		rawOutput += "[0x00000000]> # Assembling the bots\n"
+
 		// for each bot involved within the battle, we need to fetch it again, as the deep battle
 		// fech doesn't fetch that deep (it fetches the batle and the corresponding bots, but only
 		// their ids and names and not the archs and bits associated)
-		for _, b := range fullDeepBattle.Bots {
+		for i, b := range fullDeepBattle.Bots {
 			bot, err := BotGetById(b.ID)
 			if err != nil {
 				log.Println(err)
 			}
 
+			runtimeBots[i].Name = bot.Name
+
+			// TODO(emile): a bot can have multiple archs/bits, figure out what to do then
+			// I've just gone and used the first one, as a bot alwas has at least one...
+			// ...it has right?
+			runtimeBots[i].ArchName = bot.Archs[0].Name
+			runtimeBots[i].BitsName = bot.Bits[0].Name
+
 			// define the command used to assemble the bot
 			src := strings.ReplaceAll(bot.Source, "\r\n", "; ")
 			radareCommand := fmt.Sprintf("rasm2 -a %s -b %s \"%+v\"", bot.Archs[0].Name, bot.Bits[0].Name, src)
@@ -1213,14 +1464,8 @@ func battleRunHandler(w http.ResponseWriter, r *http.Request) {
 		// TODO(emile): [L] implement some kind of queue
 
 		// TODO(emile): [S] use the information given from the battle, such as the right arch and bits
-		cmd := "e asm.arch=arm"
-		output, _ := r2cmd(r2p1, cmd)
-		rawOutput += fmt.Sprintf("[0x00000000]> %s\n%s", cmd, output)
-
-		cmd = "e asm.bits=32"
-		output, _ = r2cmd(r2p1, cmd)
-		rawOutput += fmt.Sprintf("[0x00000000]> %s\n%s", cmd, output)
 
+		rawOutput += "[0x00000000]> # initializing the vm and the stack\n"
 		cmd = "aei"
 		output, _ = r2cmd(r2p1, cmd)
 		rawOutput += fmt.Sprintf("[0x00000000]> %s\n%s", cmd, output)
@@ -1230,32 +1475,55 @@ func battleRunHandler(w http.ResponseWriter, r *http.Request) {
 		rawOutput += fmt.Sprintf("[0x00000000]> %s\n%s", cmd, output)
 
 		// TODO(emile): random offsets
+		// place bots
 		for i, s := range botSources {
-			log.Printf("writing bot %d to 0x%d", i, 50*(i+1))
-			cmd := fmt.Sprintf("wx %s @ 0x%d", s, 50*(i+1))
+
+			// the address to write the bot to
+			addr := 50 * (i + 1)
+
+			// store it
+			runtimeBots[i].BaseAddr = addr
+
+			msg := fmt.Sprintf("# writing bot %d to 0x%d", i, addr)
+			rawOutput += fmt.Sprintf("[0x00000000]> %s\n", msg)
+			cmd := fmt.Sprintf("wx %s @ 0x%d", s, addr)
 			_, _ = r2cmd(r2p1, cmd)
 			rawOutput += fmt.Sprintf("[0x00000000]> %s\n", cmd)
-		}
 
-		// print the memory for some pleasing visuals
-		cmd = fmt.Sprintf("pxc 100 @ 0x50")
-		output, _ = r2cmd(r2p1, cmd) // print
-		rawOutput += fmt.Sprintf("[0x00000000]> %s\n", cmd)
-		fmt.Println(output)
+			// define the instruction point and the stack pointer
+			rawOutput += "[0x00000000]> # Setting the program counter and the stack pointer\n"
+			cmd = fmt.Sprintf("aer PC=0x%d", addr)
+			_, _ = r2cmd(r2p1, cmd)
+			rawOutput += fmt.Sprintf("[0x00000000]> %s\n", cmd)
 
-		// init stack
-		cmd = "aer PC = 0x50"
-		_, _ = r2cmd(r2p1, cmd)
-		rawOutput += fmt.Sprintf("[0x00000000]> %s\n", cmd)
+			cmd = fmt.Sprintf("aer SP=SP+0x%d", addr)
+			_, _ = r2cmd(r2p1, cmd)
+			rawOutput += fmt.Sprintf("[0x00000000]> %s\n", cmd)
 
-		cmd = "aer SP = SP + 0x50"
-		_, _ = r2cmd(r2p1, cmd)
-		rawOutput += fmt.Sprintf("[0x00000000]> %s\n", cmd)
+			// dump the registers of the bot for being able to switch inbetween them
+			// This is done in order to be able to play one step of each bot at a time,
+			// but sort of in parallel
+			rawOutput += "[0x00000000]> # Storing registers\n"
+			cmd = "aerR"
+			regs, _ := r2cmd(r2p1, cmd)
+			rawOutput += fmt.Sprintf("[0x00000000]> %s\n", cmd)
+
+			initialRegisers := strings.Replace(regs, "\n", ";", -1)
+			runtimeBots[i].Regs = initialRegisers
+		}
+
+		for i := range botSources {
+			// print the memory for some pleasing visuals
+			cmd = fmt.Sprintf("pxc 100 @ 0x%d", runtimeBots[i].BaseAddr)
+			output, _ = r2cmd(r2p1, cmd) // print
+			rawOutput += fmt.Sprintf("[0x00000000]> %s\n%s\n", cmd, output)
+		}
 
 		output, _ = r2cmd(r2p1, "pxc 100 @ 0x50") // print
 		fmt.Println(output)
 
 		// define end conditions
+		rawOutput += "[0x00000000]> # Defining the end conditions\n"
 		cmd = "e cmd.esil.todo=t theend=1"
 		_, _ = r2cmd(r2p1, cmd)
 		rawOutput += fmt.Sprintf("[0x00000000]> %s\n", cmd)
@@ -1270,35 +1538,76 @@ func battleRunHandler(w http.ResponseWriter, r *http.Request) {
 		rawOutput += fmt.Sprintf("[0x00000000]> %s\n", cmd)
 
 		// set the end condition to 0 initially
+		rawOutput += "[0x00000000]> # Initializing the end condition variable\n"
 		cmd = "f theend=0"
 		_, _ = r2cmd(r2p1, cmd)
 		rawOutput += fmt.Sprintf("[0x00000000]> %s\n", cmd)
 
+		currentBotId := 0
+
 		// TODO(emile): find a sensible default for the max amount of rounds
-		for i := 0; i < 1000; i++ {
+		for i := 0; i < fullDeepBattle.MaxRounds; i++ {
+
+			currentBotId = i % 2
 
-			// this is architecture agnostic and just outputs the program counter
 			rawOutput += fmt.Sprintf("[0x00000000]> ########################################################################\n")
+
+			// this is architecture agnostic and just gets the program counter
 			pc, _ := r2cmd(r2p1, "aer~$(arn PC)~[1]")
+
 			arch, _ := r2cmd(r2p1, "e asm.arch")
 			bits, _ := r2cmd(r2p1, "e asm.bits")
-			rawOutput += fmt.Sprintf("[0x00000000]> # ROUND %d, PC=%s, arch=%s, bits=%s\n", i, pc, arch, bits)
+			rawOutput += fmt.Sprintf("[0x00000000]> # ROUND %d, BOT %d (%s), PC=%s, arch=%s, bits=%s\n", i, currentBotId, runtimeBots[currentBotId].Name, pc, arch, bits)
+
+			rawOutput += "[0x00000000]> # setting the architecture accordingly\n"
+			cmd = fmt.Sprintf("e asm.arch=%s", runtimeBots[currentBotId].ArchName)
+			output, _ = r2cmd(r2p1, cmd)
+			rawOutput += fmt.Sprintf("[0x00000000]> %s\n%s", cmd, output)
+
+			cmd = fmt.Sprintf("e asm.bits=%s", runtimeBots[currentBotId].BitsName)
+			output, _ = r2cmd(r2p1, cmd)
+			rawOutput += fmt.Sprintf("[0x00000000]> %s\n%s", cmd, output)
+
+			// load registers
+			rawOutput += "[0x00000000]> # Loading the registers\n"
+			r2cmd(r2p1, runtimeBots[currentBotId].Regs)
+			//  rawOutput += fmt.Sprintf("%+v\n", runtimeBots[currentBotId].Regs)
+
+			//  cmd = "dr"
+			//  output, _ = r2cmd(r2p1, cmd)
+			//  rawOutput += fmt.Sprintf("[0x00000000]> %s\n%s\n", cmd, output)
 
 			//  _, _ = r2cmd(r2p1, "aes") // step
+			rawOutput += "[0x00000000]> # Stepping\n"
 			cmd = "aes"
 			_, _ = r2cmd(r2p1, cmd)
 			rawOutput += fmt.Sprintf("[0x00000000]> %s\n", cmd)
 
+			// store the regisers
+			rawOutput += "[0x00000000]> # Storing the registers\n"
+			registers, _ := r2cmd(r2p1, "aerR")
+			registersStripped := strings.Replace(registers, "\n", ";", -1)
+			runtimeBots[currentBotId].Regs = registersStripped
+
 			// print the arena
-			cmd := "pxc 100 @ 0x50"
+			rawOutput += "[0x00000000]> # Printing the arena\n"
+			cmd := fmt.Sprintf("pxc 100 @ 0x%d", runtimeBots[currentBotId].BaseAddr)
 			output, _ := r2cmd(r2p1, cmd) // print
 			rawOutput += fmt.Sprintf("[0x00000000]> %s\n%s\n", cmd, output)
-			fmt.Println(output)
+			//  fmt.Println(output)
 
-			// TODO(emile): restore state
+			// predicate - the end?
+			rawOutput += "[0x00000000]> # Checking if we've won\n"
+			pend, _ := r2cmd(r2p1, "?v theend")
+			status := strings.TrimSpace(pend)
+			// fixme: on Windows, we sometimes get output *from other calls to r2*
 
-			// TODO(emile): check the end condition
-			_, _ = r2cmd(r2p1, "?v 1+theend") // check end condition
+			if status == "0x1" {
+				log.Printf("[!] Bot %d has died", currentBotId)
+			}
+			if status != "0x0" {
+				log.Printf("[!] Got invalid status '%s' for bot %d", status, currentBotId)
+			}
 		}
 
 		BattleSaveRawOutput(battleid, rawOutput)
diff --git a/src/bot.go b/src/bot.go
index 4708fee..65c1c29 100644
--- a/src/bot.go
+++ b/src/bot.go
@@ -111,22 +111,15 @@ func (s *State) GetBotById(id int) (Bot, error) {
 
 	WHERE bo.id=?
 	GROUP BY bo.id;
-	`, id).Scan(&botid, &botname, &botsource, &ownerids, &ownernames, &archids, &archnames, &bitids, &bitnames)
+	`, id).Scan(&botid, &botname, &botsource,
+		&ownerids, &ownernames,
+		&archids, &archnames,
+		&bitids, &bitnames)
 	if err != nil {
 		log.Println(err)
 		return Bot{}, err
 	}
 
-	// log.Println("botid: ", botid)
-	// log.Println("botname: ", botname)
-	// log.Println("botsource: ", botsource)
-	// log.Println("ownerids: ", ownerids)
-	// log.Println("ownernames: ", ownernames)
-	// log.Println("archid: ", archids)
-	// log.Println("archname: ", archnames)
-	// log.Println("bitid: ", bitids)
-	// log.Println("bitname: ", bitnames)
-
 	ownerIDList := strings.Split(ownerids, ",")
 	ownerNameList := strings.Split(ownernames, ",")
 
@@ -181,6 +174,7 @@ func (s *State) GetBotById(id int) (Bot, error) {
 		log.Println("ERR4: ", err)
 		return Bot{}, err
 	default:
+		//  log.Printf("returning bot with archs %+v and bits %+v", archs, bits)
 		return Bot{botid, botname, botsource, users, archs, bits}, nil
 	}
 }
diff --git a/src/db.go b/src/db.go
index 42dc2d4..8983df9 100644
--- a/src/db.go
+++ b/src/db.go
@@ -23,8 +23,9 @@ CREATE TABLE IF NOT EXISTS battles (
 	created_at DATETIME NOT NULL,
 	name TEXT,
 	public BOOLEAN,
-	raw_output TEXT
-	max_rounds INTEGER
+	raw_output TEXT,
+	max_rounds INTEGER,
+	arena_size INTEGER
 );
 CREATE TABLE IF NOT EXISTS archs (
 	id INTEGER NOT NULL PRIMARY KEY,
diff --git a/src/user.go b/src/user.go
index 1fd9358..cbedf03 100644
--- a/src/user.go
+++ b/src/user.go
@@ -71,6 +71,10 @@ func UserGetAll() ([]User, error) {
 	return globalState.GetAllUsers()
 }
 
+func UserGetUsernameCount(username string) (int, error) {
+	return globalState.GetUsernameCount(username)
+}
+
 //////////////////////////////////////////////////////////////////////////////
 // DATABASE
 
@@ -227,6 +231,29 @@ func (s *State) GetAllUsers() ([]User, error) {
 	return users, nil
 }
 
+func (s *State) GetUsernameCount(username string) (int, error) {
+	rows, err := s.db.Query(`
+		SELECT COUNT(*)
+		FROM users
+		WHERE name=?`, username)
+	defer rows.Close()
+	if err != nil {
+		return -1, err
+	}
+
+	var count int
+	rows.Next()
+	if err := rows.Scan(&count); err != nil {
+		return -1, err
+	}
+	if err = rows.Err(); err != nil {
+		return -1, err
+	}
+	return count, nil
+}
+
+//  return globalState.GetUsernameCount(username)
+
 //////////////////////////////////////////////////////////////////////////////
 // HTTP
 
@@ -383,6 +410,13 @@ func registerHandler(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 
+		// Fetch all users and check that there isn't another user with the name here
+		if _, err := UserGetUsernameCount(username); err != nil {
+			w.WriteHeader(http.StatusInternalServerError)
+			w.Write([]byte("500 - Oi', Backend here! That username has already been taken!"))
+			return
+		}
+
 		// if we've got a password, hash it and store it and create a User
 		if password1 != "" {
 			passwordHash := argon2.IDKey([]byte(password1), []byte(os.Getenv("SALT")), 1, 64*1024, 4, 32)
diff --git a/templates/battleNew.html b/templates/battleNew.html
index 30923e7..1992e1d 100644
--- a/templates/battleNew.html
+++ b/templates/battleNew.html
@@ -50,7 +50,21 @@
           <label class="label-for-check" for="bit-{{$bit.ID}}">{{$bit.Name}}</label>
           {{- end }}
         </td>
-      </td>
+      </tr>
+
+      <tr>
+        <td>Arena size:</td>
+        <td>
+          <input class="border" type="number" name="arena-size" id="arena-size" value="4096"/>
+        </td>
+      </tr>
+
+      <tr>
+        <td>Max Rounds:</td>
+        <td>
+          <input class="border" type="number" name="max-rounds" id="max-rounds" value="100"/>
+        </td>
+      </tr>
 
       <tr>
         <td>Public:</td>
@@ -63,6 +77,23 @@
       </td>
 
       <tr>
+        <td>Owners</td>
+        <td>
+          {{ $viewerID := .user.ID }}
+          {{ range $idx, $u := .users}}{{if $idx}},{{- end}}
+            <input
+              type="checkbox"
+              class="check-with-label"
+              name="owner-{{ $u.ID }}"
+              id="owner-{{ $u.ID }}"
+              {{if eq $u.ID $viewerID}}checked{{end}}
+              />
+            <label class="label-for-check" for="owner-{{ $u.ID }}">{{$u.Name}}</label>
+          {{- end }}
+        </td>
+      </tr>
+
+      <tr>
         <td></td>
         <td><input class="border" type="submit" value="Create"></td>
       </tr>
@@ -70,11 +101,16 @@
       {{ if .res }}
       <tr>
         <td></td>
-        <td>{{ .res }}</td>
+        <td><div style="border: 1px solid blue; padding: 1ex">{{ .res }}</div></td>
       </tr>
       {{ end }}
     </form>
   </table>
+
+  <span id="debug"></span>
+  <h2><a href="#debug">Debug</a></h2>
+
+  <pre>{{ . }}</pre>
 </body>
 {{ template "footer" . }}
 {{ end }}
diff --git a/templates/battleSingle.html b/templates/battleSingle.html
index 267a0df..b074a75 100644
--- a/templates/battleSingle.html
+++ b/templates/battleSingle.html
@@ -19,7 +19,7 @@
 
   <table>
     <tbody>
-      <form id="battle" method="POST" action="/battle/{{ .battle.ID }}">
+      <form id="save" 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>
@@ -56,13 +56,6 @@
         </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>
@@ -100,92 +93,118 @@
         </tr>
 
         <tr>
-          <td></td>
+          <td>Arena size:</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>
+            <input class="border" type="number" name="arena-size" id="arena-size" value="{{ .battle.ArenaSize }}"/>
           </td>
         </tr>
-      </form>
-
-
-    {{ if .res }}
-    <tr>
-      <td></td>
-      <td><div style="border: 1px solid blue; padding: 1ex">{{ .res }}</div></td>
-    </tr>
-    {{ end }}
 
-    <tr>
-      <td><br><hr><br></td>
-      <td><br><hr><br></td>
-    </tr>
+        <tr>
+          <td>Max Rounds:</td>
+          <td>
+            <input class="border" type="number" name="max-rounds" id="max-rounds" value="100"/>
+          </td>
+        </tr>
 
-    {{ if .myBots }}
+        <tr>
+          <td>Owners</td>
+          <td>
+            {{ $viewerID := .user.ID }}
+            {{ $owners := .battle.Owners }}
+            {{ range $idx, $u := .users}}{{if $idx}},{{- end}}
+              <input
+                type="checkbox"
+                class="check-with-label"
+                name="owner-{{ $u.ID }}"
+                id="owner-{{ $u.ID }}"
+                {{if eq $u.ID $viewerID}}{{end}}
+
+                {{ range $idx, $own := $owners }}
+                  {{if eq $u.ID $own.ID}}checked{{end}}
+                {{ end }}
+                />
+              <label class="label-for-check" for="owner-{{ $u.ID }}">{{$u.Name}}</label>
+            {{- end }}
+          </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></td>
+        <td width="100%">
+          <div style="display: grid; grid-template-columns: 32% 32% 32%; justify-content: space-between;">
+            <input class="border" type="submit" value="Save Settings" form="save" style="padding: 0 1ex; width: 100%">
+            <input class="border" type="submit" value="Run Battle" form="run" style="border: width: 100%">
+            <input class="border" type="submit" value="Delete this battle" form="delete" style="border: 1px solid red; background: red; color: white; width: 100%">
+          </div>
         </td>
       </tr>
+
+      {{ if .res }}
       <tr>
-        <td colspan="5"><input class="border" type="submit" value="Submit bots"></td>
+        <td></td>
+        <td><div style="border: 1px solid blue; padding: 1ex">{{ .res }}</div></td>
       </tr>
-    </form>
-
-    {{ else }}
+      {{ end }}
 
-    <tr>
-      <td></td>
-      <td><a href='/bot/new'>Upload a bot</a> to get started!</td>
-    </tr>
+      <tr>
+        <td><br><hr><br></td>
+        <td><br><hr><br></td>
+      </tr>
 
-    {{ end }}
+      {{ if .myBots }}
 
-    </tbody>
-  <table>
+      <form id="submit" 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>
+      </form>
 
-  <br>
+      <form id="run" method="POST" action="/battle/{{ .battle.ID }}/run"> </form>
+      <form id="delete" method="POST" action="/battle/{{ .battle.ID }}/delete"></form>
 
-  <form method="POST" action="/battle/{{ .battle.ID }}/run">
-    <input class="border" type="submit" value="Run the Battle">
-  </form>
+      <tr>
+        <td></td>
+        <td width="100%">
+          <div style="display: grid; grid-template-columns: 100%; justify-content: space-between;">
+            <input class="border" type="submit" value="Submit Bots" form="submit" style="width: 100%">
+          </div>
+        </td>
+      </tr>
 
-  <br><br>
+      {{ else }}
+      <tr>
+        <td></td>
+        <td><a href='/bot/new'>Upload a bot</a> to get started!</td>
+      </tr>
+      {{ end }}
 
-  <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>
+    </tbody>
+  <table>
 
   <span id="registered bots"></span>
   <h2><a href="#registered-bots">Registered Bots</a></h2>
@@ -194,13 +213,13 @@
 
   <span id="output"></span>
   <h2><a href="#output">Output</a></h2>
-
+  <!--<details>-->
   <pre>{{ .battle.RawOutput }}</pre>
+  <!--</details>-->
 
   <span id="debug"></span>
   <h2><a href="#debug">Debug</a></h2>
+  <details> <pre>{{ . }}</pre> </details> </body>
 
-  <pre>{{ .battle }}</pre>
-</body>
 {{ template "footer" . }}
 {{ end }}