ThreadBoard ArchivesSite FeaturesActiveworlds SupportHistoric Archives |
The AW SDK *IS* Thread-Safe! (Sdk)
The AW SDK *IS* Thread-Safe! // Sdkandon13Apr 10, 2000, 10:38pm
Check this out . . . (Only relevant code has been pasted, because there's
over 120kb of code in the rest of this file, and besides, I don't distribute full source code unless there's a good reason :)). World List Sample : (Probably not Compilable, but the principle is clearly visible) UINT ONEMINUTE = 50; CImageList m_ilWorlds; CListCtrl m_Worlds; LVITEM CheckIcon; void UpdateWorldList (void); UINT RedrawWorldList (LPVOID pParam); UINT GenerateWorldList (LPVOID pParam); int BGColor, WhisperColor, FriendColor, TouristColor, PSColor, AvatarColor; CStringList PrvWrldName; CStringList PubWrldName; CStringList UnknownWrldName; CStringList PrvWrldPop; CStringList PubWrldPop; CStringList UnknownWrldPop; CTime LastUpdate; bool bShowPrivate, bShowEmpty, bRunThread = true; // bShowPrivate and bShowEmpty are read from the Registry using AfxGetApp()->GetProfileInt(...) in OnInitDialog, and then converts them from INT to BOOL. // ONEMINUTE is set to 60 seconds with SetTimer(...) in OnInitDialog . . . (DUH!) void CChatMonitor::OnTimer(UINT nIDEvent) { if ( nIDEvent == ONEMINUTE ) { CTime CurrentTime = CTime::GetCurrentTime(); CTimeSpan NextUpdate = CurrentTime - LastUpdate; if ( NextUpdate.GetMinutes() >= 5 && m_StatusTab.GetCurSel() == 1) { GetDlgItem(IDC_Worlds)->ShowWindow(SW_SHOW); // Makes sure the CListCtrl is showing. GetDlgItem(IDC_Worlds)->RedrawWindow(); // Some versions of the Common Controls DLL require this, no idea why!?!?!? AfxBeginThread(GenerateWorldList,NULL); } } CResizingDialog::OnTimer(nIDEvent); } void CChatMonitor::UpdateWorldList(void) // Used to update the World List from an External Class... { AfxBeginThread(GenerateWorldList,NULL); } void CChatMonitor::SetWorldSettings (bool ShowEmpty, bool ShowPrivate) { bShowEmpty = ShowEmpty; bShowPrivate = ShowPrivate; } UINT GenerateWorldList (LPVOID pParam) { if (!bRunThread) return 0; bRunThread = FALSE; PubWrldName.RemoveAll(); // The following lines of code clear out any OLD Worlds saved in the Linked Lists... PubWrldPop.RemoveAll(); PrvWrldName.RemoveAll(); PrvWrldPop.RemoveAll(); UnknownWrldName.RemoveAll(); UnknownWrldPop.RemoveAll(); aw_event_set (AW_EVENT_WORLD_INFO, UpdateWorldList); m_Worlds.DeleteAllItems(); int rc; if (!rc = aw_world_list()) { RedrawWorldList(NULL); LastUpdate = CTime::GetCurrentTime(); } else { LVCOLUMN ChangeTitle; ChangeTitle.mask = LVCF_TEXT; char NumberOfWorlds[10]; m_Worlds.InsertItem(0,"SDK Error",3); CString ErrorNUM; ErrorNUM.Format("%d",rc); m_Worlds.SetItemText(0,1,ErrorNUM); sprintf(NumberOfWorlds,"%d Error(s)",m_Worlds.GetItemCount()); ChangeTitle.pszText = NumberOfWorlds; ChangeTitle.cchTextMax = sizeof(NumberOfWorlds); m_Worlds.SetColumn(0,&ChangeTitle); } bRunThread = TRUE; return 0; } void UpdateWorldList (void) { char Users[4]; sprintf(Users,"%d",aw_int(AW_WORLDLIST_USERS)); switch (aw_int (AW_WORLDLIST_STATUS)) { case AW_WORLDSTATUS_PUBLIC: PubWrldName.AddTail(aw_string (AW_WORLDLIST_NAME)); PubWrldPop.AddTail(Users); break; case AW_WORLDSTATUS_PRIVATE: PrvWrldName.AddTail(aw_string (AW_WORLDLIST_NAME)); PrvWrldPop.AddTail(Users); break; default: UnknownWrldName.AddTail(aw_string (AW_WORLDLIST_NAME)); UnknownWrldPop.AddTail(Users); break; } } UINT RedrawWorldList (LPVOID pParam) { m_Worlds.DeleteAllItems(); POSITION pName = PubWrldName.GetHeadPosition(); POSITION pPop = PubWrldPop.GetHeadPosition(); while( pName != NULL ) { CString NextWorld = PubWrldName.GetNext( pName ); CString NextPop = PubWrldPop.GetNext( pPop ); int i = -1; if (!NextWorld.CompareNoCase(aw_string(AW_WORLD_NAME))) // Done because in Build 15 of the SDK the AW_WORLD_NAME is lowercase . . . i = m_Worlds.InsertItem (m_Worlds.GetItemCount(), NextWorld, 2); else if (bShowEmpty || NextPop != "0") i = m_Worlds.InsertItem (m_Worlds.GetItemCo unt(), NextWorld, 0); if (i != -1) m_Worlds.SetItemText(i,1,NextPop); } if (bShowPrivate) { POSITION pName = PrvWrldName.GetHeadPosition(); POSITION pPop = PrvWrldPop.GetHeadPosition(); while( pName != NULL ) { CString NextWorld = PrvWrldName.GetNext( pName ); CString NextPop = PrvWrldPop.GetNext( pPop ); int i = -1; if (!NextWorld.CompareNoCase(aw_string(AW_WORLD_NAME))) // Done because in Build 15 of the SDK the AW_WORLD_NAME is lowercase . . . i = m_Worlds.InsertItem (m_Worlds.GetItemCount(), NextWorld, 2); else if (bShowEmpty || NextPop != "0") i = m_Worlds.InsertItem (m_Worlds.GetItemCount(), NextWorld, 1); if (i != -1) m_Worlds.SetItemText(i,1,NextPop); } } LVCOLUMN ChangeTitle; ChangeTitle.mask = LVCF_TEXT; char NumberOfWorlds[10]; // Should probably be made into a dynamicaly sized ptr, but I didn't want to waste my time, since as of now the number of Worlds should never cause an Overflow in this, unless a bug occured. sprintf(NumberOfWorlds,"%d Worlds",m_Worlds.GetItemCount()); ChangeTitle.pszText = NumberOfWorlds; ChangeTitle.cchTextMax = sizeof(NumberOfWorlds); m_Worlds.SetColumn(0,&ChangeTitle); return 0; } Login Sample : (Probably not Compilable, but the principle is clearly visible) void CUltraBOTDlg::OnLogin() { UpdateData(); CString chNameCheck = chBotName; chNameCheck.MakeLower(); if (nCitnum <= 1 || nCitnum >= 400000) PlaySound("Audio/1stlaunch.wav",NULL,SND_ASYNC); else if (chPassword.IsEmpty() || chPassword == "change_me") PlaySound("Audio/Password.wav",NULL,SND_ASYNC); else if (chNameCheck == "ultra bot" && nCitnum != 68992) MessageBox("Sorry, but the name Ultra BOT is reserved for Andon13","Ultra BOT --- Name Conflict", MB_ICONWARNING | MB_TOPMOST); else { GetDlgItem(IDC_Login)->EnableWindow(false); Connected = false; PlaySound ("Audio/Logstart.wav",NULL,SND_ASYNC); ChatMonitor.RemoveAllAvatars(); AvatarMap.ClearMap(); LoginStorage* ptp = new LoginStorage; ptp->chPassword = chPassword; ptp->chBotName = chBotName; ptp->nCitnum = nCitnum; ptp->nAV = nAvatar; ptp->nX = nX; ptp->nY = nY; ptp->nYAW = nYAW; ptp->nZ = nZ; ptp->chWorldName = chWorld; ptp->hWnd = m_hWnd; AfxBeginThread (SecondThread, ptp); } } UINT SecondThread (LPVOID pParam) { LoginStorage* ptp = (LoginStorage*) pParam; CString chPassword = ptp->chPassword; CString chBotName = ptp->chBotName; CString chWorld = ptp->chWorldName; int nAvatar = ptp->nAV; int nX = ptp->nX; int nY = ptp->nY; int nZ = ptp->nZ; int nYAW = ptp->nYAW; int nCitnum = ptp->nCitnum; HWND hWnd = ptp->hWnd; delete ptp; bool Success = Main.Login(nCitnum, chPassword, chBotName, chWorld, nX, nY, nZ, nYAW, nAvatar); if (Success) { aw_event_set (AW_EVENT_AVATAR_ADD, handle_avatar_add); aw_event_set (AW_EVENT_AVATAR_CHANGE, handle_avatar_change); aw_event_set (AW_EVENT_AVATAR_DELETE, handle_avatar_delete); aw_event_set (AW_EVENT_CHAT, handle_chat); aw_event_set (AW_EVENT_AVATAR_CLICK, handle_avatar_click); aw_event_set (AW_EVENT_WORLD_DISCONNECT, handle_world_disconnect); CWnd::FromHandle(GetDlgItem(hWnd,IDC_Login))->ShowWindow(SW_HIDE); CWnd::FromHandle(GetDlgItem(hWnd,IDC_Logoff))->ShowWindow(SW_SHOW); } else CWnd::FromHandle(GetDlgItem(hWnd,IDC_Login))->EnableWindow(true); return 0; } bool CUltraBOTDlg::Login (int nCitnum, /* Citizen Number */ CString chPassword, /* Password */ CString chBotName, /* Bot Name */ CString chWorld, /* World Name */ int nX, /* X Co-Ord */ int nY, /* Y Co-Ord */ int nZ, /* Z Co-Ord */ int nYAW, /* Angle */ int nAvatar) /* Avatar Number */ { int rc; Connected = false; aw_destroy(); Status.Format("\r\n\r\n\r\nLogin Status:\r\nCreating Bot Instance . . ."); m_Status.SetWindowText(Status); if (rc = aw_creat e (chIP,nPort,0)) { Login_Error (rc,"BOT Instance Creation Failed"); return false; } aw_int_set (AW_LOGIN_OWNER, nCitnum); aw_string_set (AW_LOGIN_PRIVILEGE_PASSWORD, chPassword); sprintf (message,"Ultra Bot %s",chVersion); aw_string_set (AW_LOGIN_APPLICATION, message); aw_string_set (AW_LOGIN_NAME, chBotName); aw_int_set (AW_MY_X, nX); aw_int_set (AW_MY_Y, nY); aw_int_set (AW_MY_Z, nZ); aw_int_set (AW_MY_YAW, nYAW); aw_int_set (AW_MY_TYPE, nAvatar); ::nCitnum = nCitnum; ::chBotName = chBotName; Whois = false; Status.Format("\r\n\r\n\r\nLogin Status:\r\nConnecting to Universe . . ."); m_Status.SetWindowText(Status); if (rc = aw_login ()) { Login_Error (rc,"Universe Connect Failed"); return false; } Status.Format("\r\n\r\n\r\nLogin Status:\r\nConnecting to World . . ."); m_Status.SetWindowText(Status); if (rc = aw_enter (chWorld,0)) { Login_Error (rc,"World Connect Failed"); return false; } Status.Format("\r\n\r\n\r\nLogin Status:\r\nBinding to Avatar . . ."); m_Status.SetWindowText(Status); if (rc = aw_state_change ()) { Login_Error (rc,"Avatar Bind Failed"); return false; } else { PlaySound ("Audio/Logfinish.wav",NULL,SND_ASYNC); LoginTime = CTime::GetCurrentTime(); Connected = true; CTime StartTime = CTime::GetCurrentTime(); CString ChatTime = StartTime.Format("%B %#d, %Y at %#I:%M:%S %p (%Z)"); if (!ChatMonitor.bLogStarted) { CString ChatStart; ChatStart.Format("Chat log started : %s",ChatTime); ChatMonitor.Print2(ChatStart, RGB(0,0,0), 1, 0, 0, 0); ChatMonitor.bLogStarted = true; } ChatMonitor.UpdateWorldList(); ChangeWorld(); return true; } return false; } CString CUltraBOTDlg::Login_Error (int m_nError, char m_chReply2[32]) { Connected = false; switch(m_nError) { case 1:sprintf (message,"Citizen number has expired");break; case 3:sprintf (message,"Citizen number is invalid");break; case 4:sprintf (message,"Message is too long");break; case 5:sprintf (message,"Passwords cannot contain spaces");break; case 6:sprintf (message,"Password is too long");break; case 7:sprintf (message,"Password is too short");break; case 8:sprintf (message,"Range too large");break; case 9:sprintf (message,"Range too short");break; case 10:sprintf (message,"Too many users");break; case 11:sprintf (message,"Too few users");break; case 12:sprintf (message,"RC_LICENSE_WORLD_CONTAINS_SPACE");break; case 13:sprintf (message,"Invalid password");break; case 14:sprintf (message,"RC_UNABLE_TO_MAIL_BACK_NUMBER");break; case 15:sprintf (message,"RC_LICENSE_WORLD_TOO_SHORT");break; case 16:sprintf (message,"RC_LICENSE_WORLD_TOO_LONG");break; case 17:sprintf (message,"RC_SERVER_OUT_OF_MEMORY");break; case 27:sprintf (message,"World is not running");break; case 31:sprintf (message,"RC_NOT_LOGGED_IN");break; case 32:sprintf (message,"RC_UNAUTHORIZED");break; case 33:sprintf (message,"RC_ALREADY_LICENSED");break; case 34:sprintf (message,"RC_NO_SUCH_LICENSE");break; case 39:sprintf (message,"RC_IDENTITY_ALREADY_IN_USE");break; case 40:sprintf (message,"RC_UNABLE_TO_REPORT_LOCATION");break; case 41:sprintf (message,"RC_INVALID_EMAIL"); break; case 42:sprintf (message,"Citizen number doesn't exist");break; case 43:sprintf (message,"Invalid password");break; case 58:sprintf (message,"Upgrade required");break; case 59:sprintf (message,"BOT limit reached");break; case 64:sprintf (message,"RC_LICENSE_STARTS_WITH_NUMBER");break; case 66:sprintf (message,"RC_NO_SUCH_EJECTION");break; case 67:sprintf (message,"No such session number");break; case 100:sprintf (message,"RC_EMAIL_CONTAINS_INVALID_CHAR");break; case 101:sprintf (message,"RC_EMAIL_ENDS_WITH_BLANK");break; case 102:sprintf (message,"RC_EMAIL_MISSING_DOT");break; case 103:sprintf (message,"RC_EMAIL_MISSING_AT");break; case 104:sprintf (message,"RC_EMAIL_STARTS_WITH_BLANK");break; case 105:sprintf (message,"RC_EMAIL_TOO_LONG");break; case 106:sprintf (message,"RC_EMAIL_TOO_SHORT");break; case 107:sprintf (message,"RC_NAME_ALREADY_USED");break; case 108:sprintf (message,"RC_NAME_CONTAINS_INVALID_CHAR");break; case 109:sprintf (message,"RC_NAME_CONTAINS_INVALID_BLANK");break; case 110:sprintf (message,"RC_NAME_DOESNT_EXIST");break; case 111:sprintf (message,"RC_NAME_ENDS_WITH_BLANK");break; case 112:sprintf (message,"RC_NAME_TOO_LONG");break; case 113:sprintf (message,"The name : %s, is too short", chBotName);break; case 114:sprintf (message,"RC_NAME_UNUSED");break; case 115:sprintf (message,"RC_PASSWORD_TOO_LONG");break; case 116:sprintf (message,"RC_PASSWORD_TOO_SHORT");break; case 117:sprintf (message,"RC_PASSWORD_IS_WRONG");break; case 126:sprintf (message,"RC_NUMBER_ALREADY_USED");break; case 127:sprintf (message,"RC_NUMBER_OUT_OF_RANGE");break; case 128:sprintf (message,"Privilege password too short");break; case 203:sprintf (message,"RC_NOT_CHANGE_OWNER");break; case 204:sprintf (message,"RC_CANT_FIND_OLD_ELEMENT");break; case 211:sprintf (message,"RC_CANT_CHANGE_OWNER");break; case 216:sprintf (message,"RC_CANT_BUILD_HERE");break; case 232:sprintf (message,"RC_NOT_ALLOWED");break; case 300:sprintf (message,"RC_ENCROACHES");break; case 301:sprintf (message,"RC_NO_SUCH_OBJECT");break; case 302:sprintf (message,"RC_NOT_DELETE_OWNER");break; case 303:sprintf (message,"RC_TOO_MANY_BYTES");break; case 306:sprintf (message,"RC_UNREGISTERED_OBJECT");break; case 308:sprintf (message,"RC_ELEMENT_ALREADY_EXISTS");break; case 311:sprintf (message,"RC_NO_BUILD_RIGHTS");break; case 313:sprintf (message,"Object outside of world property limits");break; case 314:sprintf (message,"RC_RESTRICTED_OBJECT");break; case 400:sprintf (message,"RC_OUT_OF_MEMORY");break; case 401:sprintf (message,"RC_NOT_YET");break; case 402:sprintf (message,"TIMEOUT");break; case 403:sprintf (message,"RC_NULL_POINTER");break; case 404:sprintf (message,"Unable to contact universe");break; case 405:sprintf (message,"Unable to contact world");break; case 406:sprintf (message,"Invalid world name");break; case 415:sprintf (message,"RC_SEND_FAILED");break; case 416:sprintf (message,"RC_RECEIVE_FAILED");break; case 421:sprintf (message,"RC_STREAM_EMPTY");break; case 422:sprintf (message,"RC_STREAM_MESSAGE_TOO_LONG");break; case 423:sprintf (message,"World name too long");break; case 426:sprintf (message,"Message too long");break; case 429:sprintf (message,"Unable to connect to %s : %d",chIP,nPort);break; case 439:sprintf (message,"No connection");break; case 442:sprintf (message,"Unable to initialize network");break; case 443:sprintf (message,"Incorrect_Message_Length");break; case 444:sprintf (message,"RC_NOT_INITIALIZED");break; case 445:sprintf (message,"No BOT instance exists.");break; case 446:sprintf (message,"RC_OUT_BUFFER_FULL");break; case 447:sprintf (message,"RC_INVALID_CALLBACK");break; case 448:sprintf (message,"RC_INVALID_ATTRIBUTE");break; case 449:sprintf (message,"RC_TYPE_MISMATCH");break; case 450:sprintf (message,"RC_STRING_TOO_LONG");break; case 451:sprintf (message,"RC_READ_ONLY");break; case 453:sprintf (message,"RC_INVALID_INSTANCE");break; case 454: sprintf (message,"This version of Ultra BOT is ONLY compatible with AW.DLL build %d",AW_BUILD); GetDlgItem(IDC_Login)->EnableWindow(false); // Disables login because of incorrect AW.DLL version. break; case 461:sprintf (message,"RC_IN_BUFFER_FULL");break; case 463:sprintf (message,"RC_PROTOCOL_ERROR");break; case 464:sprintf (message,"RC_QUERY_IN_PROGRESS");break; case 466:sprintf (message,"I was ejected from this world.");break; case 467:sprintf (message,"I am not allowed to enter this world.");break; case 471:sprintf (message,"Internet connection lost");break; case 474:sprintf (message,"RC_NOT_AVAILABLE");break; default :sprintf (message,"Unknown Error (%d)!",m_nError);break; } PlaySound ("Audio/Error.wav",NULL,SND_ASYNC); Status.Format("\r\n\r\n\r\n%s\r\n\r\nReason : %s",m_chReply2,message); m_Status.SetWindowText(Status); CString Returned; Returned.Format("%s",message); return Returned; } --- END --- I didn't feel like defining the variables for the Login for ya, but I'll give you this much . . . typedef struct tagTHREADPARMS { int nCitnum; int nPort; int nX; int nY; int nZ; int nYAW; int nAV; CString chPassword; CString chBotName; CString chWorldName; CString chIP; HWND hWnd; } LoginStorage; And that should be all you need, if you're a "serious coder" :) Best Regards, Andon M. Coleman Senior Programmer R&D Nothing, Inc. P.S. Been using these methods for 6 months now . . . Left a BOT running with this for over a month on my server, not a single crash! So it's pretty safe to say that the AW SDK has proven to be Thread-Safe! bigbadbearApr 11, 2000, 7:33pm
I have one question about your code... Since it concerns real time, why
arent you using pointers instead of variables? That would increase/enhance code manipulation and time. |