Jump to content
SubSpace Forum Network

Recommended Posts

Posted (edited)

I have written a bot with chatnet protocol that conforms to the rules. For the most part. The only thing is at the end of a 4v4 round, it sends stats.

Let me explain what the bot does before you consider this. Unix asks me to right a good 4v4 bot. I don't know how the existing one works, but this one seems to work fine. I have tested it extensively and can't seem to find any bugs, but it may need some new features.

 

This is how I have it setup


// Stafflist is in config file, so players can't use this unless in the config file.
Staff CMD: !go (arena), !shutdown

Player CMD: !about, !help, !version.... (Obvious functions)
Player CMD: !rules, !time, !begin

Rules: Displays dueling rules.

Time:  Displays time remaining, (In case bot is not granted ?timer powers)

Begin: Begins a round.  If someone begins a round an no one is playing, typing !being will restart
Begin: If a game is going, then type !begin will display that a game is currently in session.

 

Those are the function, these are the commands used by the bot for better gameplay, but are not required

 

?timer   - For conventional ?time
?lock    - To keep from having ship changes, or someone jumping in the middle of the duel
?unlock  - No game in session, have fun.
?setship - Puts the player in spec so that they can't interfere with the duel

 

This bot is made so no staff member has to be present to start it. Despite the name 4v4, any team size is allowed at this time. I think team-sizes should be configured in the arena configuration anyway, but if need be I'll add it to the bot config file. There is currently no team-captain, but in a real 4v4, a staff member would probably need to put someone on a freq anyway. This bot can be used during practice or whatever.

 

I have taken all the hard work off of your hands if you will allow me to run the bot in an arena.

It has a few simple (not a security risk) commands that it would need to allow for OPTIMAL game play.

This powers would be limited to one arena if the staff.conf file. It wouldn't really matter who hosted this bot since it poses no security risk, has nothing to do with money or anything else.

 

I am asking that you consider allowing me to host a bot that is productive to the zone and makes things a little more automated. I will release full source upon request of staff. My ways of doing things might be different, but I did it in the most effective way I knew possible.

 

Small changes would need to be made to the following files...

 

Example: (conf/groupdef.dir/hsfour)

; conf/groups/hsfour

; what groups can this group control
higher_than_default

; Don't shutup when displaying chats
unlimitedchat

; prefix chars
prefix_+

; commands
cmd_lock
cmd_unlock
cmd_timer
privcmd_setship

 

Add this to File: (conf/groupdef.conf)

[hsfour]
#include groupdef.dir/default
#include groupdef.dir/hsfour

 

Then just add in (conf/staff.conf)

(arena name)
UB-DuelBot = hsfour

 

That's all you have to do. Now, the bot gets silenced when it displays stats if not granted unlimited chat, so making it have its own group is good anyway. Those few commands won't hurt anybody with arena-restriction.

 

This would be a good addition to hyperspace, wouldn't take much bandwidth (not really a big deal anyway), and uses little resources. I will add any additional features/modifications necessary and do it in a timely manner.

 

Thank you,

BD

Edited by BDwinsAlt
Posted (edited)

You should post the source.

I did a few last minute adjustments, added some comment in stuff.

I did use input[x] a few times without renaming it to something like, newfreq or something, because I would have to split strings in everycase and rename it, and if you know the protocol you know what it is anyway. I can change that if need be. I'm still learning as I go, so if you see major issues, PLEASE, point them out. I need to know for future reference. Everything I know is what I taught myself and examples I have studied. I really need constructive criticism from more experienced (sometimes lazy blum.gif) people. Oh yeah, ignore the debug stuff you'll probably notice commented out.

 

Here's my current compilation of the bot.

Source is attached if viewing is bad on your computer.

 

/*
Name: HSFour.java
Author: Brad Davis [bDwinsAlt]

Email:  BDwinsAlt@gmail.com
Website: BradDavis.info

Created: Jan 4, 2010
Modified: Jan 6, 2010 by BDwinsAlt

Description: 4v4 Bot written for Hyperspace
Protocol: Chatnet (TCP/ASSS 1.5.0rc1)
Version: 0.1a

Extra information:
	This bot is free to be used by anyone.
	I ask that you leave me in the !version tag, add yourself if you like.
	Make sure you have permission before running a bot in another zone.
	If you see something that looks weird, I drink, so email me if I did something goofy.
	Include line number(s) and attach source file as you have it.
	Do not use this software for malicious purposes of any kind.
*/

import java.io.*;
import java.net.*;
import java.util.*;

public class HSFour {

public static void main(String[] args) throws IOException {

	// Used for stats and such.
	Map kills = new HashMap();	// Number of kills
	Map deaths = new HashMap();	// Number of deaths
	Map freq = new HashMap();	// Frequency of players

	// Used for !time function
	long startTime = 0;

	// Socket and Input/Output Stream
	Socket socket = null;
	PrintWriter out = null;
	BufferedReader in = null;

	// Create info for later
	String loginName = null;
	String loginPass = null;
	String loginHost = null;
	int loginPort = 5000;
	String loginArena = null;
	String loginChat= null;
	String staffList = null;
	boolean inSession = false;

	try {

		// Properties load the config file
		Properties configFile = new Properties();
		configFile.load(new FileInputStream("BotConfig.txt"));

		// Load the information needed
		loginName = configFile.getProperty("Name");
		loginPass = configFile.getProperty("Password");
		loginHost = configFile.getProperty("Host"); // Can be URL or IP
		loginPort = Integer.valueOf(configFile.getProperty("Port")); // Port is an integer, so just convert it here
		loginArena = configFile.getProperty("Arena"); // Default Arena
		loginChat = configFile.getProperty("Chat"); // Chat channel
		staffList = configFile.getProperty("Staff");

	} catch(Exception p) {
		System.out.println("[File] Unable to read BotConfig.txt.  Make sure this file exists in your folder exactly as it appear here.");
	}

	try {

		// Create the socket and connect
		socket = new Socket(loginHost, loginPort);

		// Create the Input/Output stream
		out = new PrintWriter(socket.getOutputStream(), true);
		in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

		// Tell the bot to login once it establishes a connection
		System.out.println("[Connect] Established a connection with server...\n");

		out.println("LOGIN:chatnet:" + loginName + ":" + loginPass);

	} catch (UnknownHostException e) {
		System.err.println("[Connect] Unable to establish connection.  Unknown host: " + loginHost);
		System.exit(1);
	} catch (IOException e) {
		System.err.println("[Connect] Unable to establish I/O connection to host: " + loginHost);
		System.exit(1);
	}

	String fromServer;
	while ((fromServer = in.readLine()) != null) {

		// Debug purposes
		// System.out.println(fromServer+"\n");

		// In case of dumbasses, it won't crash the bot, it will ignore it.
		try{
			String input[] = fromServer.split(":");

			// PRIVATE MESSAGE HANDLER
			if(input[1].equals("PRIV")) {

				// Names and such, makes things easier
				String name = input[2];
				String cmd = input[3];
				String send = "SEND:PRIV:" + name + ":";

				// COMMAND HANDLER
				if(cmd.equalsIgnoreCase("!help")){
					out.println(send + "4v4Bot: !help, !about, !version");
					out.println(send + "4v4Bot: !go (arena), !begin, !time, !rules");

				} else if(cmd.equalsIgnoreCase("!version")) {
					out.println(send + "4v4Bot 0.1a created by BDwinsAlt");

				} else if(cmd.equalsIgnoreCase("!about")) {
					out.println(send + "I am a simple 4v4Bot created for hyperspace using chatnet. " + 
					"I record kills and display stats at the end of each round.");

				} else if(cmd.equalsIgnoreCase("!time")) {

					if(System.currentTimeMillis() - startTime < 600000 && System.currentTimeMillis() - startTime > 0) {
						long myTime = 600000 - (System.currentTimeMillis() - startTime);
						String minutes = Integer.toString((int)(myTime % (1000*60*60)) / (1000*60));
						String seconds = Integer.toString((int)((myTime % (1000*60*60)) % (1000*60)) / 1000);

						String msg = "Time remaining: Minutes: " + minutes + "  Seconds: " + seconds; 
						out.println(send + msg);
					} else {
						out.println(send + "Currently not in a game.");
					}

					// Check if a player is staff and ?go
				} else if(cmd.startsWith("!go")) {
					if(staffList.contains("," + name + ",")) {
						out.println("GO:" + input[3].substring(4) + "\r\n");
						loginArena = input[3].substring(4);
					} else {
						out.println(send + "Unauthorized access");
					}

					// Check if a player is staff and shutdown
				} else if(cmd.startsWith("!shutdown")) {
					if(staffList.contains("," + name + ",")) {
						out.println(send + "Shutting down bot...");
						System.out.println("Shutting down bot at request of " + name);
						break;
					} else {
						out.println(send + "Unauthorized access");
					}

					// Begin a match if one is not started.
				} else if(cmd.startsWith("!begin") && System.currentTimeMillis() - startTime > 60000) {
					// Reset everyone to default values, in case someone was killed prior to !begin
					Iterator el = kills.keySet().iterator();
					while (el.hasNext()) {
						Object key = el.next();
						kills.put(key, 0);
						deaths.put(key, 0);
					}

					startTime = System.currentTimeMillis();

					out.println(send + "Timer set for 10 minutes");
					out.println("SEND:PUB:?grplogin hsfour k");
					out.println("SEND:PUB:?|lock|timer 10\r\n");
					out.println("SEND:PUB:Game is set.  You may begin.  Use !time to see how much time is remaining.");

					inSession = true;

				} else if(cmd.startsWith("!begin")) {
					out.println(send + "You can't begin a game while one is in progress.");

					int freq0 = 0;
					int freq1 = 0;

					// Check how many people are on each frequency
					Iterator it = freq.values().iterator(); 
					while (it.hasNext()) {
						Object value = it.next();

						// Find number of people on freq 0
						if(Integer.valueOf(value.toString()) == 0) {
							freq0 += 1;
						}

						// Find number of people on freq 1
						if(Integer.valueOf(value.toString()) == 1)
							freq1 += 1;						
					}

					if(freq0 == 0 || freq1 == 0) {
						startTime = 0;
						inSession = false;
						out.println("SEND:PUB:?|unlock|timer 0");
						out.println("SEND:PUB:It seems no one is actually playing.  Resetting bot.");
						out.println("GO:" + loginArena);
					}

				} else if(cmd.startsWith("!rules")){
					out.println(send + "RULES:  Do not change ships while playing.  After five deaths you are out.  Last team standing wins.");

					// Information on commands goes here.
				} else if(cmd.equalsIgnoreCase("!help go")) {
					out.println(send + "Syntax:  !go <arena>");
					out.println(send + "Example: !go duel");
					out.println(send + "Purpose: Sends the bot to an arena");
				} else if(cmd.equalsIgnoreCase("!help set")) {
					out.println(send + "Syntax:  !begin");
					out.println(send + "Example: !begin");
					out.println(send + "Purpose: Begins the round. If allowed, Locks arena");
				} else if(cmd.equalsIgnoreCase("!help time")) {
					out.println(send + "Syntax:  !time");
					out.println(send + "Example: !time");
					out.println(send + "Purpose: Displays how much time is left in the duel");
				} else if(cmd.equalsIgnoreCase("!help about")) {
					out.println(send + "Syntax:  !about");
					out.println(send + "Example: !about");
					out.println(send + "Purpose: Displays information about the bot.");
				} else if(cmd.equalsIgnoreCase("!help version")) {
					out.println(send + "Syntax:  !version");
					out.println(send + "Example: !version");
					out.println(send + "Purpose: Sends version information.");	
				} else if(cmd.equalsIgnoreCase("!help rules")) {
					out.println(send + "Syntax:  !rules");
					out.println(send + "Example: !rules");
					out.println(send + "Purpose: Displays a list of rules.");
				}

				// KILL HANDLER	
			} else if(fromServer.startsWith("KILL") && inSession == true) {
				try{

					// Get the number of kills the killer has and add 1
					int killer = Integer.valueOf(kills.get(input[1]).toString()) + 1;
					kills.put(input[1], killer);

					// Get the number of deaths the killed player has and add 1
					int killed = Integer.valueOf(deaths.get(input[2]).toString()) + 1;
					deaths.put(input[2], killed);

					// Send who killed whom
					// out.println("SEND:PUB:Killer: " + input[1] + "   Killed: " + input[2]);

					// Send messages to chat channel
					out.println("SEND:CHAT:" + input[1] + ": " + kills.get(input[1]).toString() + ".  Deaths: " + deaths.get(input[1]).toString());
					out.println("SEND:CHAT:" + input[2] + ": " + kills.get(input[2]).toString() + ".  Deaths: " + deaths.get(input[2]).toString());

					// If the player has died 5 times, get rid of him.
					if(Integer.valueOf(deaths.get(input[2]).toString()) == 5) {

						// Number of people on each frequency determined later on
						int freq0 = 0;
						int freq1 = 0;

						// When someone reaches maximum number of deaths
						out.println("SEND:PUB:" + input[2] + " has exhausted his/her lives. R.I.P.");
						out.println("SEND:PRIV:" + input[2] + ":?setship 9");
						out.println("SEND:PRIV:" + input[2] + ":You may not kill or die, stay out of the way until the match is done.");

						// Remove this player as active on the freq to recall who is left.
						freq.remove(input[2]);

						// Check how many people are on each frequency
						Iterator it = freq.values().iterator(); 
						while (it.hasNext()) {
							Object value = it.next();

							// Find number of people on freq 0
							if(Integer.valueOf(value.toString()) == 0) {
								freq0 += 1;
							}

							// Find number of people on freq 1
							if(Integer.valueOf(value.toString()) == 1)
								freq1 += 1;						
						}

						if(freq0 >= 1 && freq1 == 0 || freq1 >= 1 && freq0 == 0) {
							// space is used to format the output correctly
							String space = "                     ";

							out.println("SEND:PUB:+----------------------------------------------+");
							out.println("SEND:PUB:| Name                   |  Kills  |  Deaths   |");
							out.println("SEND:PUB:+----------------------------------------------+");

							// Print stats for each player
							Iterator el = kills.keySet().iterator();
							while (el.hasNext()) {
								Object key = el.next();

								// Number of kills and deaths per player
								String killstat = kills.get((key).toString()).toString();
								String deathstat = deaths.get((key).toString()).toString();

								// calculate the number of spaces needed and print stats, don't edit.
								out.println("SEND:PUB:| " + key.toString() + space.substring(key.toString().length() - 2) + "|    " + killstat + "    |    " + deathstat + "      |");
							} 

							out.println("SEND:PUB:+----------------------------------------------+");

							if(freq0 >= 1) {
								out.println("SEND:PUB:---> Freq 0 Wins! <---");
							} else {
								out.println("SEND:PUB:---> Freq 1 Wins! <---");
							}

							inSession = false;
							out.println("SEND:PUB:?unlock");

							// Reload all player data without having to Iterate through everything, this is simpler
							out.println("GO:" + loginArena);
						}

					}
				}catch(Exception e){}


				// TIMER ENDS. NO ONE WON. Possibly change this.
			} else if(fromServer.equalsIgnoreCase("MSG:ARENA:Notice: Game over") && inSession == true) {
				inSession = false;
				String space = "                     ";

				out.println("SEND:PUB:+----------------------------------------------+");
				out.println("SEND:PUB:| Name                   |  Kills  |  Deaths   |");
				out.println("SEND:PUB:+----------------------------------------------+");

				// Print stats for each player
				Iterator el = kills.keySet().iterator();
				while (el.hasNext()) {
					Object key = el.next();

					// Number of kills and deaths per player
					String killstat = kills.get((key).toString()).toString();
					String deathstat = deaths.get((key).toString()).toString();

					// calculate the number of spaces needed and print stats, don't edit.
					out.println("SEND:PUB:| " + key.toString() + space.substring(key.toString().length() - 2) + "|    " + killstat + "    |    " + deathstat + "      |");
				} 

				out.println("SEND:PUB:+----------------------------------------------+");
				out.println("---> Draw! Time limit has been reached. <--");
				out.println("SEND:CMD:?unlock");
				out.println("GO:" + loginArena);

				// PLAYER ENTERS OR WAS ALREADY THERE, CREATE BLANK STATS
			} else if(fromServer.startsWith("PLAYER:") || fromServer.startsWith("ENTERING:")) {

				// Assign each player a fresh start on stats
				if(input[3].equals("0") || input[3].equals("1")) {
					kills.put(input[1], 0);
					deaths.put(input[1], 0);
					freq.put(input[1], input[3]);	
				}

				// SHIP/FREQ Change, required so bot knows if someone joins after the bot logs in.
			} else if(fromServer.startsWith("SHIPFREQ")) {
				//NOTE: This is why the arena need to be locked, it overwrites.
				if(input[3].equals("0") || input[3].equals("1")) {
					kills.put(input[1], 0);
					deaths.put(input[1], 0);
					freq.put(input[1], input[3]);	
				}

				// PLAYER LEAVES
			} else if(fromServer.startsWith("LEAVING:")) {
				kills.remove(input[1]);
				deaths.remove(input[1]);
				freq.remove(input[1]);

				// LOGIN SUCCESSFUL, NOW GO TO AN ARENA
			} else if(fromServer.startsWith("LOGINOK")) {
				out.println("GO:" + loginArena);
				System.out.println("[Login]   Successfully Logged in as " + loginName + " on " + loginHost);

				// LOGIN HAD BADPASSWORD
			} else if(fromServer.startsWith("LOGINBAD")) {
				System.out.println("[Login]   Invalid password for " + loginName);

				// JOINED ARENA, NOW SET CHATS
			} else if(fromServer.startsWith("INARENA")) {
				startTime = 0;
				out.println("SEND:PUB:?chat=" + loginChat);


				// Keep tabs with ASSS
			} else if(fromServer.equals("NOOP")) {
				out.println("NOOP");
			}

		}catch(Exception lol){}
	}
	out.close();
	in.close();
	socket.close();
}
}

 

Example BotConfig.txt

# Edit information below
# This is all self-explanitory

Name=myname
Password=mypassword
Host=127.0.0.1
Port=5000
Arena=#4v4
Chat=HS4v4

# Start and end with commas, no spaces (easier to code, get over it)
Staff=,BDwinsAlt,Dr Brain,D1st0rt,Ceiu,Raretanium,Rivel,Masaru,

Edited by BDwinsAlt
Posted

Interesting approach. Some notes:

 

- For a simple bot like this, this design is probably okay. But, I'd imagine it'll get real messy in a hurry once you start adding other features. Perhaps consider a more OOP design in the future?

- I wasn't aware of the Properties class and that it could be used for some simple configuration. Neat.

- As of 1.5, Java supports printf style formatting via PrintStream.printf(...) and String.format(...) methods as well as the Formatter class. You may want to check that out.

- What happens when a player dies less than five times, lags out, quits or otherwise doesn't finish? Also, can they be subbed?

- Speaking of things you should check out, are you aware of jcnlib? It implements the entire chatnet protocol for you, as well as provides some utilities. If you're going to use Java for a chatnet bot, this may save you some time. Also, the 4v4 bot I made is written around it, which you can check out here.

Posted (edited)

Thanks for the tips. I didn't realize you had already made a 4v4 bot. I thought it was a mervbot plugin or something. I'll take all that into consideration. I haven't added a sub or anything yet, that's a feature I may need to add. I had a bot a lot like this in TWCore a while back used for squad duels. I can't believe you've never run across my form of loading properties. It's makes things über easy. I do think I should have broken it up a little. It's all in one thread and I usually like to break things apart more and stop using functions over and over. It is functional, so making those modifications would help. It's a stand-alone bot, so it may or may not be too important to use an already existing library, it might be easier. I'll have to check it out.

 

Thanks for the tips.

 

Question: Have you taken any programming classes? If so, what do you recommend because I plan on pursuing a career in some type of computer-related job.

 

Edit: I tried to download your 4v4 bot but I can't extract it. Not really sure why. I'll try again after I get some sleep.

Thanks again. :)

Edited by BDwinsAlt

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...