BDwinsAlt Posted January 6, 2010 Report Posted January 6, 2010 (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 January 6, 2010 by BDwinsAlt Quote
BDwinsAlt Posted January 6, 2010 Author Report Posted January 6, 2010 (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 ) 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 January 6, 2010 by BDwinsAlt Quote
»Ceiu Posted January 6, 2010 Report Posted January 6, 2010 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. Quote
BDwinsAlt Posted January 6, 2010 Author Report Posted January 6, 2010 (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 January 6, 2010 by BDwinsAlt Quote
spidernl Posted January 9, 2010 Report Posted January 9, 2010 - I wasn't aware of the Properties class and that it could be used for some simple configuration. Neat. Neat indeed. Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.