Author | Message | Time |
---|---|---|
dave | Hi everyone, I've already searched the forum, but could'nt find any helpful solution for me. :-\ I'm searching a source code for a C# BNET channel bot. A easy form-based example project, which is using MBNCSUtil or B# would make me really happy. I've already found this: http://www.jinxbot.net/wiki/index.php?title=How_to_develop_a_Battle.net_client_in_10_minutes But since it's only command line and an old version, it doesn't help me much. Even I couldn't get a form-based application to work with this tutorial. So, if someone got a example project or a source code of a working bot for me, you guys would make me the happiest guy in this forum! Thanks in advance!!! Best Regards Dave | March 20, 2009, 5:52 PM |
dlStevens | No offense Dave but if you couldn't put a battle.net bot together by comparing a console-based bot then its not the battle.net inners that's your problem.. it's the OOP and C# it's self.. you should probably check out some tutorials for GUI based things in C# instead of battle.net - Dale | March 20, 2009, 7:07 PM |
Myndfyr | Most likely, what you're running into is problems handling cross-thread events. As the BN# documentation notes for every event: [quote]Events in the JinxBot API are never guaranteed to be executed on the UI thread. Events that affect the user interface should be marshaled back to the UI thread by the event handling code. Generally, high-priority event handlers are raised on the thread that is parsing data from Battle.net, and lower-priority event handler are executed from the thread pool.[/quote] I personally handle the fact that BN# is multi-threaded by encapsulating that behavior within the UI elements. So for instance, in my chat display: [code] // Checks to see if Invoke is required to add the chat nodes to the window. private void AddChatPrivate(List<ChatNode> nodes) { for (int i = 0; i < nodes.Count; i++) { if (nodes[i] == null) throw new ArgumentNullException("nodes"); } if (InvokeRequired) BeginInvoke(AddChatImplementation, nodes); else AddChatImplementation(nodes); } [/code] In my channel list: [code] private delegate void Invokee<T>(T args); void JoinedChannel(object sender, ServerChatEventArgs e) { if (InvokeRequired) Invoke(new Invokee<ServerChatEventArgs>(ClearChannelList), e); else ClearChannelList(e); } void UserFlagsUpdated(object sender, UserEventArgs e) { if (m_voidView && "The Void".Equals(m_client.ChannelName, StringComparison.Ordinal)) /* void view */ { if (InvokeRequired) BeginInvoke(new Invokee<UserEventArgs>(ShowUser), e); else ShowUser(e); } } void UserShown(object sender, UserEventArgs e) { if (InvokeRequired) BeginInvoke(new Invokee<UserEventArgs>(ShowUser), e); else ShowUser(e); } void UserLeft(object sender, UserEventArgs e) { if (InvokeRequired) BeginInvoke(new Invokee<UserEventArgs>(RemoveUser), e); else RemoveUser(e); } [/code] Using InvokeRequired, BeginInvoke and Invoke are the standard practice for handling cross-thread UI calls in .NET because Windows may only update UI elements from the UI thread. | March 20, 2009, 7:58 PM |
dave | Hey MyndFyre[vL] , you pointed it out, this is exactly my problem using the B# documentation. Gonna try to implent it the way you've explained it. Thanks a lot!!! Regards Dave | March 20, 2009, 10:02 PM |
-MichaeL- | MyndFyre, Could you explain your above code. I'm having a little bit of trouble understanding it. The current code i use to add to my RichTextBox seems to be fail compared to what you use however i am not sure how yours works unless of course i am misunderstanding what you posted which is also possible. My Current Code [code] public class message { public System.Drawing.Color c; public string txt; public message(System.Drawing.Color c, string txt) { this.c = c; this.txt = txt; } } public void rtbADD(System.Windows.Forms.RichTextBox r, params Asgard.Structs.message[] o) { string result = string.Format("[{0}] ", DateTime.Now.ToLongTimeString()); if (r.InvokeRequired) { Forms.frmBot.EmptyDelegate del1 = delegate() { rtbADD(r, o); }; r.Invoke(del1); } else { r.SelectionStart = r.TextLength; r.SelectionLength = 0; r.SelectionColor = Color.White; r.SelectedText = result; for (int i = 0; i != o.Length; i++) { r.SelectionStart = r.TextLength; r.SelectionLength = 0; r.SelectionColor = o[i].c; if (i == (o.Length - 1)) { r.SelectedText = o[i].txt + Environment.NewLine; } else { r.SelectedText = o[i].txt; } } if (r.Lines.Length >= 500) { r.SelectionStart = 0; r.SelectionLength = r.Text.IndexOf('\n') + 1; r.SelectedText = " "; r.SelectionStart = 0; r.SelectionLength = r.Text.IndexOf(']'); r.SelectedText = r.SelectedText.Substring(1); r.SelectionStart = r.Text.Length; r.ScrollToCaret(); } else { r.SelectionStart = r.TextLength; r.ScrollToCaret(); } } } } and on my frmMain form i have public delegate void EmptyDelegate(); [/code] | March 21, 2009, 2:18 AM |
Myndfyr | You have to understand that my code is more object-oriented than yours, in that my library has a class called "DisplayBox" and another called "ChatBox" (ChatBox wraps DisplayBox), and that each instance of one of these objects contains its own rich text display (I actually use a WebBrowser control). I don't need to pass in a RichTextBox (or WebBrowser in my case) as a parameter because it is contained by the object on which the AddChat function is being called. DisplayBox has an overloaded function called AddChat. These overloads package up their parameters nicely for AddChatPrivate (shown in my previous post). AddChatPrivate then calls AddChatImplementation, potentially using the Windows Forms invoke/thread-marshaling semantics as necessary. This is nice because each method in the chain has a single responsibility, though it creates some additional overhead as multiple methods need to be called. The same general practice is used in my listbox control. Each event handler checks to see whether invoking needs to happen and calls the domain-specific function appropriately, invoking if necessary. I really need to spend some time creating an architecture diagram about how JinxBot works, because one of my goals is to use it to teach other people. | March 21, 2009, 3:18 AM |
dave | Hey guys, just tried to build it the way MyndFyre explained it. But somehow I'm not able to get it to work. Never worked with threads this way, only used the background worked and got it always to work somehow. ... and even don't know how "Invoke" works. Maybe someone of you guys could tell me, what I have to change. Here is the code without "Invoke". Sorry. I hope I'm not annoying. Thanks in advance. Best Regards, Dave [code] using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using BNSharp; using BNSharp.Net; using BNSharp.BattleNet; using BNSharp.MBNCSUtil; using BNSharp.Plugins; namespace DIV_Gamelist_Bot { public partial class Form1 : Form { public Form1() { InitializeComponent(); } BattleNetClient client; string bnet_chat_message = ""; class Settings : IBattleNetSettings { #region IBattleNetSettings Members public string CdKey1 { get { return ""; } set { } } public string CdKey2 { get; set; } public string CdKeyOwner { get { return "YOU DONT KNOW"; } set { } } public string Client { get { return "WAR3"; } set { } } public string GameExe { get { return @"Game\War3.exe"; } set { } } public string GameFile2 { get { return @"Game\Storm.dll"; } set { } } public string GameFile3 { get { return @"Game\Game.dll"; } set { } } public string ImageFile { get; set; } public string Password { get; set; } public PingType PingMethod { get; set; } public int Port { get; set; } public string Server { get; set; } public string Username { get; set; } public int VersionByte { get { return 0x17; } set { } } #endregion } private void Form1_Load(object sender, EventArgs e) { string realm = txtB_bnet_realm.Text; int rport = Convert.ToInt32(txtB_bnet_port.Text); string rockey = txtB_bnet_key.Text; string user = txtB_bnet_user.Text; string pwd = txtB_bnet_pass.Text; Settings set = new Settings() { CdKey1 = rockey, Server = realm, Port = rport, Username = user, Password = pwd }; client = new BattleNetClient(set); client.Connected += delegate { bnet_chat_add("--- CONNECTED"); }; client.Error += new ErrorEventHandler(client_Error); client.EnteredChat += new EnteredChatEventHandler(client_EnteredChat); client.LoginSucceeded += new EventHandler(client_LoginSucceeded); client.LoginFailed += new LoginFailedEventHandler(client_LoginFailed); client.ServerBroadcast += new ServerChatEventHandler(client_ServerBroadcast); client.ServerErrorReceived += new ServerChatEventHandler(client_ServerErrorReceived); client.UserShown += new UserEventHandler(client_UserShown); client.UserJoined += new UserEventHandler(client_UserJoined); client.UserLeft += new UserEventHandler(client_UserLeft); client.UserSpoke += new ChatMessageEventHandler(client_UserSpoke); client.UserEmoted += new ChatMessageEventHandler(client_UserEmoted); client.ClientCheckPassed += delegate { bnet_chat_add("--- VERSIONING PASSED"); }; client.ClientCheckFailed += new ClientCheckFailedEventHandler(client_ClientCheckFailed); client.JoinedChannel += new ServerChatEventHandler(client_JoinedChannel); client.WardentUnhandled += delegate { bnet_chat_add("--- WARNING: Warden requested and unhandled!!"); }; client.MessageSent += new ChatMessageEventHandler(client_MessageSent); client.Disconnected += delegate { bnet_chat_add("--- DISCONNECTED"); }; bnet_chat_add("Battle.Net - Bot Settings loaded."); } #region BNET EVENTHANDLERS private void client_MessageSent(object sender, ChatMessageEventArgs e) { if (e.EventType == ChatEventType.Emote) { bnet_chat_add("<{0} {1}>" + e.Username + e.Text); } else { bnet_chat_add("[{0}]: {1}" + e.Username + e.Text); } } private void client_JoinedChannel(object sender, ServerChatEventArgs e) { bnet_chat_add("CHANNEL: {0}" + e.Text); } private void client_ClientCheckFailed(object sender, ClientCheckFailedEventArgs e) { bnet_chat_add("--- VERSIONING FAILED {0}:" + e.Reason); bnet_chat_add(e.AdditionalInformation); } private void client_UserEmoted(object sender, ChatMessageEventArgs e) { bnet_chat_add("<{0} {1}>" + e.Username + e.Text); } private void client_UserSpoke(object sender, ChatMessageEventArgs e) { bnet_chat_add("{0}: {1}" + e.Username + e.Text); } private void client_UserLeft(object sender, UserEventArgs e) { bnet_chat_add("USER LEFT: {0}" + e.User.Username); } private void client_UserJoined(object sender, UserEventArgs e) { bnet_chat_add("USER JOIN: {0} ({1})" + e.User.Username + e.User.Stats); } private void client_UserShown(object sender, UserEventArgs e) { bnet_chat_add("USER: {0} ({1})" + e.User.Username + e.User.Stats); } private void client_ServerErrorReceived(object sender, ServerChatEventArgs e) { bnet_chat_add("SERVER ERROR: {0}" + e.Text); } private void client_ServerBroadcast(object sender, ServerChatEventArgs e) { bnet_chat_add("SERVER: {0}" + e.Text); } private void client_LoginFailed(object sender, EventArgs e) { bnet_chat_add("--- LOGIN FAILED:"); } private void client_LoginSucceeded(object sender, EventArgs e) { bnet_chat_add("--- LOGIN SUCCEEDED"); } private void client_EnteredChat(object sender, EnteredChatEventArgs e) { bnet_chat_add("Entered chat as {0}" + e.UniqueUsername); } private void client_Error(object sender, ErrorEventArgs e) { bnet_chat_add("ERROR: {0}" + e.Error); } #endregion private void btn_bnet_connect_Click(object sender, EventArgs e) { client.Connect(); string text; bool exit = false; do { text = bnet_chat_message; bnet_chat_message = ""; if (!text.Equals("/exit", StringComparison.Ordinal)) { client.SendMessage(text); } else { exit = true; } } while (!exit); client.Close(); bnet_chat_add("Disconnected :("); } private void bnet_chat_add(string text) { lstB_bnet_chat.Items.Add(text); } private void txtB_bnet_msg_KeyPress(object sender, KeyPressEventArgs e) { if (e.KeyChar == (char)13) bnet_chat_message = txtB_bnet_msg.Text; txtB_bnet_msg.Text = ""; } } } [/code] | March 21, 2009, 2:05 PM |
fataly | Addchat function... Public Sub AddChat(Optional ByVal TextColor As Long = vbWhite, Optional ByVal strText As String = "", Optional ByVal ClearTextLenOver As Long = 50000) With Form1.rtbChat If Len(.Text) > ClearTextLenOver Then .Text = "" .SelStart = Len(.Text) .SelColor = TextColor .SelText = .SelText & strText .SelStart = Len(.Text) End With End Sub But, it can do like this: text1.text = text1.text + "What ever text you wanna type" | March 21, 2009, 4:13 PM |
dave | Hey, thanks for your answer! But before adding some "add chat text" function I need to build up a working connection to BNET, and so far this doesn't work :/ Somehow I managed to get the invoke working. [code] delegate void del(string text); private void bnet_chat_add(string text) { if (InvokeRequired) { del d = new del(bnet_chat_add); this.Invoke(d, new object[] { text }); } lstB_bnet_chat.Items.Add(text); } [/code] But as soon as I try to connect, the application freezes and I get a error message which says something like: "No data can be written into the transmission connection: An existing connection was closed by the remote host." Someone got an idea? Regards Dave | March 21, 2009, 4:36 PM |
-MichaeL- | This may not be the problem but most of your code doesn't format the string correctly. [code] bnet_chat_add("<{0} {1}>" + e.Username + e.Text); [/code] your code will be outputted as "<{0} {1}>UsernameText" You need to use [code] string.Format("<{0} {1}>", e.Username , e.Text); [/code] and that will output "<Username Text>" This may cause your problem however i could be wrong. | March 21, 2009, 6:00 PM |
Myndfyr | Well your CdKey1 property is returning an empty string, so that's not good. It could just be an automatic property like you've implemented CdKey2, since you try to set it yourself. But the specific error message you're getting means you've been banned. | March 21, 2009, 7:51 PM |