diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2792f41 --- /dev/null +++ b/.gitignore @@ -0,0 +1,221 @@ +# ---> VisualStudio +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +/Ashita.exp +/Ashita.lib diff --git a/Servo.sln b/Servo.sln new file mode 100644 index 0000000..e69f5aa --- /dev/null +++ b/Servo.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30110.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Servo", "Source\Servo.vcxproj", "{19191E0E-AA6C-4BAB-800E-06ADFCB1ED5B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {19191E0E-AA6C-4BAB-800E-06ADFCB1ED5B}.Debug|Win32.ActiveCfg = Debug|Win32 + {19191E0E-AA6C-4BAB-800E-06ADFCB1ED5B}.Debug|Win32.Build.0 = Debug|Win32 + {19191E0E-AA6C-4BAB-800E-06ADFCB1ED5B}.Release|Win32.ActiveCfg = Release|Win32 + {19191E0E-AA6C-4BAB-800E-06ADFCB1ED5B}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Source/Chat.cpp b/Source/Chat.cpp new file mode 100644 index 0000000..00ca939 --- /dev/null +++ b/Source/Chat.cpp @@ -0,0 +1,52 @@ +#include "Chat.hpp" + +namespace Chat{ + void Log::putAutoTransOpen() + { + s.put('\xFF'); + s.put('\xEF'); + s.put('\x27'); + } + + void Log::putAutoTransClose() + { + s.put('\xFF'); + s.put('\xEF'); + s.put('\x28'); + } + + IAutoTrans Log::Region(unsigned char RegionID) + { + IAutoTrans i; + i.x = m_Ashita->GetResourceManager()->GetString("regions", RegionID, 0); + return i; + } + + IAutoTrans Log::Zone(unsigned int ZoneID) + { + IAutoTrans i; + i.x = m_Ashita->GetResourceManager()->GetString("areas",ZoneID,0); + return i; + } + + IAutoTrans Log::Keyitem(unsigned int ID) + { + IAutoTrans i; + i.x = m_Ashita->GetResourceManager()->GetString("keyitems",ID,0); + return i; + } + + Log::Log() : m_Ashita(NULL) + { + mode = RecvdSay; + s = std::ostringstream(); + } + Log::~Log() + { + } + + void Log::SetCore(IAshitaCore* mAshitaCore) + { + m_Ashita = mAshitaCore; + } +}; \ No newline at end of file diff --git a/Source/Chat.hpp b/Source/Chat.hpp new file mode 100644 index 0000000..9d0694c --- /dev/null +++ b/Source/Chat.hpp @@ -0,0 +1,438 @@ +#include +#include +#include +#include "G:/Code/git.ashita.atom0s.com/Ashita v3/Ashita-src/build/plugins/ADK/Ashita.h" + +#include + +namespace Chat +{ + //typedef const char* IAutoTrans; + struct IAutoTrans{ + const char* x; + }; + + enum Mode : short + { + // Info mostly taken from http://www.ffevo.net/wiki/index.php/ChatIDs + + //---------------------- + // Common "Chat" Modes + //---------------------- + Unknown = 0, // Catch all. Do not use...it sometimes breaks the chat log. + SentSay = 0x1, // Outgoing Say + SentSay2 = 0x2, // Outgoing Say + SentTell = 0x4, // Outgoing Tell + SentParty = 0x5, // Outgoing Party + SentLinkShell = 0x6, // Outgoing Linkshell + SentEmote = 0x7, // Outgoing Emote, untargetted + RecvdSay = 0x9, // Incoming Say + RecvdSay2 = 0xA, // Incoming Say + RcvdTell = 0xC, // Incoming Tell + RcvdParty = 0xD, // Incoming Party (thanks Vanion for the correction!) + RcvdLinkShell = 0xE, // Incoming Linkshell + SentEmoteTo = 0xF, // Outgoing Emote, targeted + Echo = 0xCE, // Echo + + //---------------------- + // Action Modes + //---------------------- + + ActCallHelp = 0x10, // Call for Help! + ActFriendReq = 0x12, // Sent Friend Request + ActCastMagic = 0x13, // + ActHitDamage = 0x14, // + ActMiss = 0x15, // + ActDrained = 0x16, // + ActPTHitAbsorbed = 0x17, // + ActPTRecoverHP = 0x18, // + ActPTHitDamage = 0x19, // + ActPTMiss = 0x1A, // + ActPTDrained = 0x1B, // + ActDamaged = 0x1C, // + ActAvoidDamage = 0x1D, // + ActRecoverHealth = 0x1E, // + ActRecoverHP = 0x1F, // + ActPTDamaged = 0x20, // + ActPTAvoidDamage = 0x21, // + ActPCGainHealth = 0x22, // + ActPTHealedOther = 0x23, // + ActDefeats = 0x24, // + ActPCDeath = 0x25, // + ActDeath = 0x26, // + ActPTDeath = 0x27, // + ActPCDamage = 0x28, // + ActPCMiss = 0x29, // + ActPCRecoverHealth = 0x2A, // + ActPCHealsPC = 0x2B, // + ActPCDefeated = 0x2C, // + + //---------------------- + // Special Action Modes + //---------------------- + + SAct = 0x32, // + SActPT = 0x33, // + SActPC = 0x34, // + SActRecvdBenefit = 0x38, // + SActRecvdDetriment = 0x39, // + SActNoEffect = 0x3B, // + SActPTRecvdBenefit = 0x3C, // + SActPTRecvdDetriment = 0x3D,// + SActPTNoEffect = 0x3F, // Needs confirmation + SActPCRecvdBenefit = 0x40, // + SActPCRecvdDetriment = 0x41,// + SActPCNoEffect = 0x43, // + SActPTResisted = 0x44, // + SActOtherResisted = 0x45, // + + SActSAttUse = 0x65, // + SActOtherBenefit = 0x6F, // + SActOtherDetriment = 0x70, // + SActOtherMissed = 0x72, // + + SActSpellEffect = 0xBB, // + + //---------------------- + // Item Modes + //---------------------- + + ItemRecvdEffect = 0x51, // + ItemPCUsed = 0x55, // + ItemPCUsed2 = 0x5A, // + ItemLearnedSpell = 0x5B, // + + //---------------------- + // Mob Special Action Modes + //---------------------- + + MobActSAttUse = 0x64, // + MobActDetriment = 0x66, // + MobActSAttEvaded = 0x68, // + MobActSAttAoE = 0x69, // + MobActSupport = 0x6A, // + MobActDrain = 0x6B, // + MobActRAttMissed = 0x6D, // + MobActSAttInit = 0x6E, // + + //---------------------- + // System Message Modes + //---------------------- + + Sys1 = 0x79, // Target out of range, AH & synth feedback, Item feedback, trade feedback + Sys2 = 0x7A, // Entity chasing out of range, entity intimidated, raise, paralyzed, cannot see entity, + // casting interrupted, cannot attack, target already claimed, no exp gained + Sys3 = 0x7B, // Sneak/invis wearing, wait longer to perform action, chat not sent/recvd + SysObtainedItem = 0x7F, // + SysSkillGained = 0x81, // + SysExpLvlMerit = 0x83, // + SysInvitedToParty = 0x87, // + SysLogoutCountdown = 0x88, // + SysBazaar = 0x8A, // + SysTimePlayed = 0x8B, // + SysClock = 0x8C, // + SysDynamisWarning = 0x8D, // + SysTaunts = 0x8E, // + SysTutorialMoogle = 0x90, // + SysCaughtFish = 0x92, // + Sys4 = 0x94, // Digging/Fishing/Door/Ballista Feedback, Mission Gil Reward, Accepted Mission, Receive + // Mission Key Item, Gained Conquest Points + SysTimeRestriction = 0x95, // + SysTutorialNPC = 0x98, // + SysCommandError = 0x9D, // Also /help output + SysConquestUpdate = 0xA1, // + + SysNeedKey = 0xBE, // + Sys5 = 0xBF, // Effect wears off, Detriment wears off, no longer stunned, fall to level + SysMOTD = 0xC8, // Also recast times (but not /recast feedback) + SysSearchComment = 0xCC, // + SysLinkshellMOTD = 0xCD, // + SysExamined = 0xD0, // + + //---------------------- + // Alliance Action Modes + //---------------------- + + AllyHealAlly = 0xA2, // + AllyDamage = 0xA3, // + AllyMiss = 0xA4, // + AllyDefeats = 0xA6, // + AllyDefeated = 0xA7, // + AllySAct = 0xA8, // + AllyNoEffect = 0xAA, // + AllyItemUse = 0xAB, // + AllyRecvdDetriment = 0xAE, // + AllyRecvdBenefit = 0xAF, // + + AllyRecvdDetrimentAoE = 0xB6,// + AllyDamaged = 0xB9, // + AllyEvades = 0xBA, // + + //---------------------- + // NPC Action Modes + //---------------------- + + NPCSAct = 0xB1, // + + NPCActDetriment = 0xB5, // + + //---------------------- + // Unknown or Unused Modes + //---------------------- + + SentYell = 0x03, + RecvdYell = 0x11, + //0x3 + //0x8 + //0xB + //0x11 + //0x2D-0x31 + //0x35-0x37 + //0x3A + //0x3E + //0x42 + //0x46-0x50 + //0x52-0x54 + //0x56-0x59 + //0x5C-0x63 + //0x67 + //0x6C + //0x71 + //0x73-0x78 + //0x7C-0x7E + //0x80 + //0x82 + //0x84-0x86 + //0x89 + //0x8F + //0x91 + //0x93 + //0x96 + //0x97 + //0x99-0x9C + //0x9E-0xA0 + //0xA5 + //0xA9 + //0xAC + //0xAD + //0xB0 + //0xB2-0xB4 + //0xB7 + //0xB8 + //0xBC + //0xBD + //0xC0-0xC7 + //0xC9-0xCB + //0xCF + //0xD1-0xFF + }; + + enum Format { + LineBreak,// = 0x07, // Inserts an inline line break (does not flush) + Reset,// = 0x1E01, + Green,// = 0x1E02, + Blue,// = 0x1E03, + Purple,// = 0x1E05, + Aqua,// = 0x1E06, + Peach,// = 0x1E07, + White,// = 0x1E1A, + Gray,// = 0x1E1B, + Lilac,// = 0x1E3A, + BlueWhite,// = 0x1E3C, + InvisibleBlue,// = 0x1E62, // Ridiculously hard to read + Red,// = 0x1E69, + Yellow,// = 0x1E6E, + RoyalBlue,// = 0x1E71, + Invisible,// = 0x1E86, // This one really is invisible as far as I can tell + GreenWhite,// = 0x1E96, + SageGreen,// = 0x1EA0, + AutoTransOpen,// = 0xFFEF27, + AutoTransClose,// = 0xFFEF28, + EnterToContinue,// = 0x7F3101 // Waits one second before making prompt available. Can alter the last byte to change the timing. + }; + + enum Control + { + flush, //Flush the output to chat log and clears the buffer + clear, //Clears the buffer without writing it out + }; + + class Log{ + private: + Mode mode; + std::ostringstream s; + IAshitaCore* m_Ashita; + + void putAutoTransOpen(); + void putAutoTransClose(); + public: + Log &operator<<(Chat::Mode x) + { + mode = x; + return *this; + } + + Log &operator<<(Chat::Format x) + { + switch (x) + { + case LineBreak: + s.put('\x07'); + break; + case Reset: + s.put('\x1E'); + s.put('\x01'); + break; + case Green: + s.put('\x1E'); + s.put('\x02'); + break; + case Blue: + s.put('\x1E'); + s.put('\x03'); + break; + case Purple: + s.put('\x1E'); + s.put('\x05'); + break; + case Aqua: + s.put('\x1E'); + s.put('\x06'); + break; + case Peach: + s.put('\x1E'); + s.put('\x07'); + break; + case White: + s.put('\x1E'); + s.put('\x1A'); + break; + case Gray: + s.put('\x1E'); + s.put('\x1B'); + break; + case Lilac: + s.put('\x1E'); + s.put('\x3A'); + break; + case BlueWhite: + s.put('\x1E'); + s.put('\x3C'); + break; + case InvisibleBlue: + s.put('\x1E'); + s.put('\x62'); + break; + case Red: + s.put('\x1E'); + s.put('\x69'); + break; + case Yellow: + s.put('\x1E'); + s.put('\x6E'); + break; + case RoyalBlue: + s.put('\x1E'); + s.put('\x71'); + break; + case Invisible: + s.put('\x1E'); + s.put('\x86'); + break; + case GreenWhite: + s.put('\x1E'); + s.put('\x96'); + break; + case SageGreen: + s.put('\x1E'); + s.put('\xA0'); + break; + case AutoTransOpen: + putAutoTransOpen(); + break; + case AutoTransClose: + putAutoTransClose(); + break; + case EnterToContinue: + s.put('\x7F'); + s.put('\x31'); + s.put('\x01'); + break; + } + return *this; + } + + Log &operator<<(ISpell* x) + { + putAutoTransOpen(); + s << x->Name; + putAutoTransClose(); + return *this; + } + + Log &operator<<(IAbility* x) + { + putAutoTransOpen(); + s << x->Name; + putAutoTransClose(); + return *this; + } + + Log &operator<<(IItem* x) + { + putAutoTransOpen(); + s << x->Name; + putAutoTransClose(); + return *this; + } + + Log &operator<<(Chat::IAutoTrans x) + { + putAutoTransOpen(); + s << x.x; + putAutoTransClose(); + return *this; + } + + Log &operator<<(Chat::Control x) + { + switch(x) + { + case Control::flush: + m_Ashita->GetChatManager()->AddChatMessage(mode,s.str().c_str()); + case Control::clear: + s = std::ostringstream(); + break; + } + return *this; + } + + Log &operator<<(const char* &x) + { + s << x; + return *this; + } + + Log &operator<<(std::string &x) + { + s << x; + return *this; + } + + Log &operator<<(const char x[]) + { + s << x; + return *this; + } + + Log(); + ~Log(); + + IAutoTrans Region(unsigned char RegionID); + IAutoTrans Zone(unsigned int ZoneID); + IAutoTrans Keyitem(unsigned int ID); + + void SetCore(IAshitaCore* mAshitaCore); + }; + +}; \ No newline at end of file diff --git a/Source/CommandParser.h b/Source/CommandParser.h new file mode 100644 index 0000000..faa1fb5 --- /dev/null +++ b/Source/CommandParser.h @@ -0,0 +1,79 @@ +#include +#include + +class CommandParser +{ +private: + std::vector thisList; + uint8_t argC; + uint8_t argIdx; +public: + CommandParser() + {} + ~CommandParser() + {} + + void InputCommand(const char* szBuffer); + bool GetFirstCommand(std::string*); + bool GetCommandAt(unsigned int,std::string*); + bool GetNextCommand(std::string*); + bool GetRemainingCommands(std::string*); + unsigned int GetArgCount(); +}; + +void CommandParser::InputCommand(const char* szBuffer) +{ + thisList.clear(); + argIdx = 0; + argC = 0; + uint8_t span = 0; + char* szTemp = const_cast(szBuffer); + char* tokenBuf; + for(char* substr = strtok_s(szTemp, " ",&tokenBuf);substr; substr = strtok_s(NULL, " ",&tokenBuf)) + { + thisList.push_back(substr); + argC += 1; + } +} + +bool CommandParser::GetFirstCommand(std::string* sReturn) +{ + argIdx = 0; + return GetNextCommand(&*sReturn); +} + +bool CommandParser::GetCommandAt(unsigned int idx, std::string* sReturn) +{ + argIdx = idx; + return GetNextCommand(&*sReturn); +} + +bool CommandParser::GetNextCommand(std::string* sReturn) +{ + if(argIdx < argC) + { + *sReturn = thisList[argIdx]; + argIdx += 1; + return true; + } + return false; +} + +bool CommandParser::GetRemainingCommands(std::string* sReturn) +{ + bool retVal = false; + *sReturn = ""; + while(argIdx < argC) + { + sReturn->append(thisList[argIdx]); + sReturn->append(1,'\x20'); + argIdx += 1; + retVal = true; + } + return retVal; +} + +unsigned int CommandParser::GetArgCount() +{ + return argC; +} \ No newline at end of file diff --git a/Source/Exports.def b/Source/Exports.def new file mode 100644 index 0000000..19ba87b --- /dev/null +++ b/Source/Exports.def @@ -0,0 +1,5 @@ +LIBRARY "Servo" +EXPORTS + GetInterfaceVersion + CreatePluginInfo + CreatePlugin \ No newline at end of file diff --git a/Source/Servo.h b/Source/Servo.h new file mode 100644 index 0000000..97ba2c5 --- /dev/null +++ b/Source/Servo.h @@ -0,0 +1,97 @@ +#ifndef __ASHITA_SERVO_H_INCLUDED__ +#define __ASHITA_SERVO_H_INCLUDED__ + +#if defined (_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include +#include +#include +#include +#include "zmq.hpp" +#include "G:/Code/git.ashita.atom0s.com/Ashita v3/Ashita-src/build/plugins/ADK/Ashita.h" +#include "CommandParser.h" +#include "Chat.hpp" + +plugininfo_t* g_PluginInfo = NULL; + +#pragma comment(lib,"libzmq.lib") + +struct auto_follow +{ + unsigned int unknown_ptr; + unsigned int TargetIndex; + unsigned int TargetID; + float DirectionX; + float DirectionY; + float DirectionZ; + float unknown_float; // 1 -- Deals with collision (CXiCollisionActor) + unsigned int unknown_ptr2; + unsigned int FollowIndex; + unsigned int FollowID; + unsigned char CameraMode; // 0 = third-person - 1 = first-person + unsigned char AutoRun; + +}; + +namespace ServoMode { + enum Mode { Standby, Server, Client }; +}; + +class Servo : IPlugin +{ +private: + IAshitaCore* m_AshitaCore; + Chat::Log log; + std::string me; + + auto_follow* StructPointer; + + zmq::context_t zContext; + + std::mutex lock; + std::atomic_bool connected; + std::atomic_bool follow; + std::atomic_bool zoning; + std::vector groups; + std::thread conn; + std::thread pos; + std::deque buffer; + + std::atomic state; + std::string servAddr; + + std::mutex lservAddr; + + std::string StringSub(std::string, std::string, std::string); + + CommandParser* cmdParse; + void formatCommand(std::string* command); + void SendCommand(std::string command); + void RecvCommand(std::string* command); + void SetState(ServoMode::Mode mode); + void Sync(std::string host); + + void WalkTo(float x, float y); + + //Threads + void Serv(); + void ServPos(); + void Client(); + void Follow(); +public: + Servo() {} + ~Servo() {} + plugininfo_t GetPluginInfo(void) override; + + bool Initialize(IAshitaCore* ashitaCore, ILogManager* log, uint32_t dwPluginId) override; + void Release(void) override; + bool HandleCommand(const char* pszCommand, int nCommandType) override; + bool Direct3DInitialize(IDirect3DDevice8* lpDevice) override; + void Direct3DRender(void) override; + bool HandleIncomingPacket(uint16_t id, uint32_t size, void* data, void* modified, bool blocked) override; + bool HandleOutgoingPacket(uint16_t id, uint32_t size, void* data, void* modified, bool blocked) override; +}; + +#endif //__ASHITA_SERVO_H_INCLUDED__ \ No newline at end of file diff --git a/Source/Servo.vcproj b/Source/Servo.vcproj new file mode 100644 index 0000000..7c307ec --- /dev/null +++ b/Source/Servo.vcproj @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/Servo.vcxproj b/Source/Servo.vcxproj new file mode 100644 index 0000000..095f427 --- /dev/null +++ b/Source/Servo.vcxproj @@ -0,0 +1,114 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {19191E0E-AA6C-4BAB-800E-06ADFCB1ED5B} + Servo + Win32Proj + + + + DynamicLibrary + MultiByte + true + v140_xp + + + DynamicLibrary + MultiByte + v140_xp + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + false + + + $(SolutionDir)$(Configuration)\Plugins\ + + + + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;SERVO_EXPORTS;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + ProgramDatabase + zeromq\include;%(AdditionalIncludeDirectories) + Async + true + + + Exports.def + true + Windows + MachineX86 + libci.lib + + + + + Full + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;SERVO_EXPORTS;%(PreprocessorDefinitions) + MultiThreadedDLL + false + + + Level3 + ProgramDatabase + zeromq\include;%(AdditionalIncludeDirectories) + Async + true + true + + + true + Windows + true + true + MachineX86 + libci.lib + Exports.def + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/libzmq.lib b/Source/libzmq.lib new file mode 100644 index 0000000..e5b487b Binary files /dev/null and b/Source/libzmq.lib differ diff --git a/Source/main.cpp b/Source/main.cpp new file mode 100644 index 0000000..2afb087 --- /dev/null +++ b/Source/main.cpp @@ -0,0 +1,594 @@ +#include "Servo.h" +#include + +#pragma comment(lib, "Psapi.lib") +#include + +#include +#include +#include +#include + +// trim from start (in place) +static inline void ltrim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), + std::not1(std::ptr_fun(std::isspace)))); +} + +// trim from end (in place) +static inline void rtrim(std::string &s) { + s.erase(std::find_if(s.rbegin(), s.rend(), + std::not1(std::ptr_fun(std::isspace))).base(), s.end()); +} + +// trim from both ends (in place) +static inline void trim(std::string &s) { + ltrim(s); + rtrim(s); +} + +// trim from start (copying) +static inline std::string ltrimmed(std::string s) { + ltrim(s); + return s; +} + +// trim from end (copying) +static inline std::string rtrimmed(std::string s) { + rtrim(s); + return s; +} + +// trim from both ends (copying) +static inline std::string trimmed(std::string s) { + trim(s); + return s; +} + + + +std::string Servo::StringSub(std::string instring, std::string subin, std::string subout) +{ + if (instring.find(subin) != std::string::npos) + return(instring.replace(instring.find(subin), subin.length(), subout)); + else + return instring; +} + +void Servo::formatCommand(std::string* command) +{ + std::stringstream id; + id << m_AshitaCore->GetDataManager()->GetTarget()->GetTargetServerId(); + + *command = StringSub(*command, "[me]", m_AshitaCore->GetDataManager()->GetParty()->GetMemberName(0)); + *command = StringSub(*command, "[p0]", m_AshitaCore->GetDataManager()->GetParty()->GetMemberName(0)); + *command = StringSub(*command, "[p1]", m_AshitaCore->GetDataManager()->GetParty()->GetMemberName(1)); + *command = StringSub(*command, "[p2]", m_AshitaCore->GetDataManager()->GetParty()->GetMemberName(2)); + *command = StringSub(*command, "[p3]", m_AshitaCore->GetDataManager()->GetParty()->GetMemberName(3)); + *command = StringSub(*command, "[p4]", m_AshitaCore->GetDataManager()->GetParty()->GetMemberName(4)); + *command = StringSub(*command, "[p5]", m_AshitaCore->GetDataManager()->GetParty()->GetMemberName(5)); + *command = StringSub(*command, "[t]", id.str()); +} + +void Servo::SendCommand(std::string command) +{ + while (!lock.try_lock()) + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + buffer.push_back(command); + lock.unlock(); +} + +void Servo::WalkTo(float x, float z) +{ + if (zoning) + return; + + auto myX = m_AshitaCore->GetDataManager()->GetEntity()->GetLocalX(m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0)); + auto myZ = m_AshitaCore->GetDataManager()->GetEntity()->GetLocalZ(m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0)); + auto myStatus = m_AshitaCore->GetDataManager()->GetEntity()->GetStatus(m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0)); + float dist = sqrt(pow((myX - x), 2.0f) + pow((myZ - z), 2.0f)); + if (dist > 1.5f && (myStatus == 0 || myStatus == 5)) + { + if (StructPointer->AutoRun != 1) + StructPointer->AutoRun = 1; + StructPointer->DirectionX = x - myX; + StructPointer->DirectionZ = z - myZ; + StructPointer->DirectionY = 0; + } + else + StructPointer->AutoRun = 0; +} + +void Servo::RecvCommand(std::string* command) +{ + command->erase(); + if (lock.try_lock()) + { + if (!buffer.empty()) + { + command->swap(buffer.front()); + buffer.pop_front(); + } + lock.unlock(); + } +} + +void Servo::SetState(ServoMode::Mode mode) +{ + ServoMode::Mode prevState = state; + state = ServoMode::Mode::Standby; + follow = false; + zoning = false; + connected = false; + if (prevState != ServoMode::Mode::Standby) + { + conn.join(); + pos.join(); + } + while (!buffer.empty()) + buffer.pop_front(); + state = mode; +} + +void Servo::Sync(std::string host) +{ + me = m_AshitaCore->GetDataManager()->GetParty()->GetMemberName(0); + log << Chat::Format::RoyalBlue << "Servo started in client mode, connected to: "; + while (!lservAddr.try_lock()) + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + if (host != "") + { + servAddr = host; + log << servAddr; + } + else + { + servAddr = ""; + log << "localhost"; + } + lservAddr.unlock(); + + SetState(ServoMode::Mode::Client); + conn.swap(std::thread(&Servo::Client, this)); + pos.swap(std::thread(&Servo::Follow, this)); + log << Chat::Control::flush; +} + +bool Servo::Initialize(IAshitaCore* ashitaCore, ILogManager* logManager, uint32_t dwPluginId) +{ + this->m_AshitaCore = ashitaCore; + this->m_PluginId = dwPluginId; + + // Obtain the base module information.. + MODULEINFO mod = { 0 }; + if (!::GetModuleInformation(::GetCurrentProcess(), ::GetModuleHandle("FFXiMain.dll"), &mod, sizeof(MODULEINFO))) + return false; + + // Cast the data and scan for the pattern.. + std::vector data((uint8_t*)(uintptr_t)mod.lpBaseOfDll, (uint8_t*)(uintptr_t)mod.lpBaseOfDll + mod.SizeOfImage); + + unsigned char* Pointer = (unsigned char*)Ashita::Memory::FindPattern(std::ref(data), (uintptr_t)mod.lpBaseOfDll, "8BCDE87F7????F8B0D7F7F7F7FE87F7F7F7F8BF885??750CB9", 0, 0); + StructPointer = NULL; + if (Pointer != NULL) + { + + Pointer += 25; + StructPointer = (auto_follow*)(*((DWORD*)Pointer)); + } + + if (StructPointer) + { + StructPointer->DirectionX; + StructPointer->DirectionZ; + StructPointer->AutoRun; + } + + cmdParse = new CommandParser; + state = ServoMode::Mode::Standby; + lservAddr.lock(); + servAddr = ""; + lservAddr.unlock(); + me = ""; + follow = false; + zoning = false; + + zContext = zmq::context_t(1); + conn = std::thread(); + pos = std::thread(); + + log.SetCore(m_AshitaCore); + log << Chat::Mode::Echo; + + return 1; +} + +void Servo::Serv() +{ + zmq::socket_t publisher(zContext, ZMQ_PUB); + std::string hostmask = "tcp://"; + while (!lservAddr.try_lock()) + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + if (!servAddr.empty()) + hostmask.append(servAddr); + else + hostmask.append("127.0.0.1"); + lservAddr.unlock(); + hostmask.append(":56556"); + publisher.bind(hostmask.c_str()); + + connected = true; + + while (connected) { + while (!lock.try_lock()) + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + if (!buffer.empty()) + { + auto it = buffer.begin(); + zmq::message_t message(it->length() + 1); + strcpy_s((char*)message.data(), (size_t)(it->length() + 1), it->c_str()); + buffer.pop_front(); + lock.unlock(); + publisher.send(message, ZMQ_DONTWAIT); + } + else + lock.unlock(); + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } + publisher.close(); +} + +void Servo::ServPos() +{ + zmq::socket_t publisher(zContext, ZMQ_PUB); + std::string hostmask = "tcp://"; + while (!lservAddr.try_lock()) + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + if (!servAddr.empty()) + hostmask.append(servAddr); + else + hostmask.append("127.0.0.1"); + lservAddr.unlock(); + hostmask.append(":56557"); + publisher.bind(hostmask.c_str()); + + if (!connected) + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + while (connected) { + if (!zoning && m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0)) + { + auto myX = m_AshitaCore->GetDataManager()->GetEntity()->GetLocalX(m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0)); + auto myZ = m_AshitaCore->GetDataManager()->GetEntity()->GetLocalZ(m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0)); + + std::stringstream ss; + ss << m_AshitaCore->GetDataManager()->GetParty()->GetMemberZone(0) << " " << myX << " " << myZ; + zmq::message_t message(strlen(ss.str().c_str()) + 1); + strcpy_s((char*)message.data(), strlen(ss.str().c_str()) + 1, ss.str().c_str()); + publisher.send(message, ZMQ_DONTWAIT); + } + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + publisher.close(); +} + +void Servo::Client() +{ + std::string host = "tcp://"; + while (!lservAddr.try_lock()) + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + if (!servAddr.empty()) + host.append(servAddr); + else + host.append("localhost"); + lservAddr.unlock(); + host.append(":56556"); + zmq::socket_t subscriber(zContext, ZMQ_SUB); + subscriber.connect(host.c_str()); + subscriber.setsockopt(ZMQ_SUBSCRIBE, me.c_str(), me.size()); + for (auto it = groups.begin(); it != groups.end(); ++it) + subscriber.setsockopt(ZMQ_SUBSCRIBE, it->c_str(), it->size()); + subscriber.setsockopt(ZMQ_SUBSCRIBE, "::", 2); + subscriber.setsockopt(ZMQ_SUBSCRIBE, ":SERVOFUNC:", 11); + connected = true; + + while (connected) + { + zmq::message_t update; + + while (subscriber.recv(&update, ZMQ_DONTWAIT) == false) + { + if (connected) + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + else + break; + } + + std::string s(static_cast(update.data())); + if (!s.empty()) + { + while (!lock.try_lock()) + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + buffer.push_back(s); + lock.unlock(); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } + subscriber.close(); +} + +void Servo::Follow() +{ + std::string host = "tcp://"; + while (!lservAddr.try_lock()) + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + if (!servAddr.empty()) + host.append(servAddr); + else + host.append("localhost"); + lservAddr.unlock(); + host.append(":56557"); + zmq::socket_t subscriber(zContext, ZMQ_SUB); + subscriber.connect(host.c_str()); + subscriber.setsockopt(ZMQ_SUBSCRIBE, "", 0); + + if (!connected) + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + while (connected) + { + zmq::message_t update; + while (follow) + { + update = zmq::message_t(); + while (subscriber.recv(&update, ZMQ_DONTWAIT) == false) + { + if (connected) + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + else + break; + } + std::stringstream ss(static_cast(update.data())); + if (!ss.str().empty()) + { + float WARPX, WARPY; + short zoneid; + ss >> zoneid >> WARPX >> WARPY; + if (zoneid == m_AshitaCore->GetDataManager()->GetParty()->GetMemberZone(0)) + WalkTo(WARPX, WARPY); + else + StructPointer->AutoRun = 0; // If no longer sharing the same zone, stop running (will trigger after the server char has arrived in the new area, so there is some delay) + } + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + + while (subscriber.recv(&update, ZMQ_DONTWAIT) == false) + { + if (connected & !follow) + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + else + break; + } + + update.data(); // Discard data when not following. + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + subscriber.close(); +} + +void Servo::Release() +{ + SetState(ServoMode::Mode::Standby); + zContext.close(); + delete cmdParse; +} + +plugininfo_t Servo::GetPluginInfo(void) +{ + return (*g_PluginInfo); +} + +bool Servo::HandleCommand(const char* szCommand, int iType) +{ + if (iType == 0) + { + return false; + } + + char szClean[1024]; + if (iType != -2) + m_AshitaCore->GetChatManager()->ParseAutoTranslate(szCommand, szClean, 1024, false); + else + strncpy_s(szClean, szCommand, 1024); + + std::string arg; + + cmdParse->InputCommand(szClean); + cmdParse->GetFirstCommand(&arg); + + if (arg == "/servo") + { + cmdParse->GetNextCommand(&arg); + if (arg == "on") + { + log << Chat::Format::RoyalBlue << "Servo started in server mode, with a hostmask of: "; + while (!lservAddr.try_lock()) + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + if (cmdParse->GetNextCommand(&arg)) + { + servAddr = arg; + log << servAddr; + } + else + { + servAddr = ""; + log << "localhost"; + } + lservAddr.unlock(); + + SetState(ServoMode::Mode::Server); + conn.swap(std::thread(&Servo::Serv, this)); + pos.swap(std::thread(&Servo::ServPos, this)); + log << Chat::Control::flush; + } + else if (arg == "sync") + { + if (!cmdParse->GetNextCommand(&arg)) + arg = ""; + Sync(arg); + } + else if (arg == "off") + { + SetState(ServoMode::Mode::Standby); + log << Chat::Format::RoyalBlue << "Servo set to standy!" << Chat::Control::flush; + } + else if (arg == "command" || arg == "send") + { + if (cmdParse->GetRemainingCommands(&arg)) + { + arg = ":: " + arg; + formatCommand(&arg); + SendCommand(arg); + } + + } + else if (arg == "sendto") + { + if (cmdParse->GetRemainingCommands(&arg)) + { + formatCommand(&arg); + SendCommand(arg); + } + + } + else if (arg == "followme") + { + if (state.load() == ServoMode::Mode::Server) + { + if (!cmdParse->GetNextCommand(&arg)) + { + if (arg == "on") + SendCommand(":SERVOFUNC: FOLLOWON"); + else if (arg == "off") + SendCommand(":SERVOFUNC: FOLLOWOFF"); + } + else + SendCommand(":SERVOFUNC: FOLLOW"); + } + } + else if (arg == "follow") + { + if (state.load() == ServoMode::Mode::Client) + { + if (!cmdParse->GetNextCommand(&arg)) + { + if (arg == "on") + follow = true; + else if (arg == "off") + follow = false; + } + else + follow = follow ? false : true; + } + } + else if (arg == "addgroup") + { + std::string group; + cmdParse->GetNextCommand(&group); + groups.push_back(group); + if (state == ServoMode::Mode::Client) + Sync(servAddr); + } + else if (arg == "delgroup") + { + bool changed = false; + std::string group; + cmdParse->GetNextCommand(&group); + for (auto it = groups.begin(); it != groups.end();) + { + if (*it == group) + { + changed = true; + it = groups.erase(it); + } + else + ++it; + } + if (state == ServoMode::Mode::Client && changed) + Sync(servAddr); + } + return true; + } + return false; +} + +bool Servo::Direct3DInitialize(IDirect3DDevice8* mDevice) +{ + return true; +} + +void Servo::Direct3DRender() +{ + if (state.load() == ServoMode::Mode::Client) + { + std::string commandtype; + std::string incommand; + RecvCommand(&incommand); + if (!incommand.empty()) + { + commandtype = incommand.substr(0, incommand.find_first_of(" ")); + incommand = incommand.substr(incommand.find_first_of(" ") + 1); + if (commandtype != ":SERVOFUNC:") + { + auto cmd = incommand; + trim(cmd); + m_AshitaCore->GetChatManager()->QueueCommand(cmd.c_str(), (int32_t)Ashita::CommandInputType::Menu); + } + else + { + if (incommand == "FOLLOWON") + follow = true; + else if (incommand == "FOLLOWOFF") + follow = false; + else if (incommand == "FOLLOW") + follow = follow ? false : true; + } + } + } +} + +bool Servo::HandleOutgoingPacket(uint16_t id, uint32_t size, void* data, void* modified, bool blocked) +{ + if (id == 0x5E) + zoning = true; + return false; +} + +bool Servo::HandleIncomingPacket(uint16_t id, uint32_t size, void* data, void* modified, bool blocked) +{ + if (id == 0x0A) + zoning = false; + return false; +} + +__declspec(dllexport) double __stdcall GetInterfaceVersion(void) +{ + return ASHITA_INTERFACE_VERSION; +} + +__declspec(dllexport) void __stdcall CreatePluginInfo(plugininfo_t* lpBuffer) +{ + g_PluginInfo = lpBuffer; + + strcpy_s(g_PluginInfo->Name, sizeof(g_PluginInfo->Name), "Servo"); + strcpy_s(g_PluginInfo->Author, sizeof(g_PluginInfo->Author), "bluekirby0"); + + g_PluginInfo->InterfaceVersion = ASHITA_INTERFACE_VERSION; + g_PluginInfo->PluginVersion = 3.00f; + g_PluginInfo->Priority = 0; +} + +__declspec(dllexport) IPlugin* __stdcall CreatePlugin(void) +{ + return (IPlugin*)new Servo(); +} diff --git a/Source/zeromq/include/zmq.h b/Source/zeromq/include/zmq.h new file mode 100644 index 0000000..f7b10db --- /dev/null +++ b/Source/zeromq/include/zmq.h @@ -0,0 +1,416 @@ +/* + Copyright (c) 2007-2013 Contributors as noted in the AUTHORS file + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . + + ************************************************************************* + NOTE to contributors. This file comprises the principal public contract + for ZeroMQ API users (along with zmq_utils.h). Any change to this file + supplied in a stable release SHOULD not break existing applications. + In practice this means that the value of constants must not change, and + that old values may not be reused for new constants. + ************************************************************************* +*/ + +#ifndef __ZMQ_H_INCLUDED__ +#define __ZMQ_H_INCLUDED__ + +/* Version macros for compile-time API version detection */ +#define ZMQ_VERSION_MAJOR 4 +#define ZMQ_VERSION_MINOR 0 +#define ZMQ_VERSION_PATCH 3 + +#define ZMQ_MAKE_VERSION(major, minor, patch) \ + ((major) * 10000 + (minor) * 100 + (patch)) +#define ZMQ_VERSION \ + ZMQ_MAKE_VERSION(ZMQ_VERSION_MAJOR, ZMQ_VERSION_MINOR, ZMQ_VERSION_PATCH) + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined _WIN32_WCE +#include +#endif +#include +#include +#if defined _WIN32 +#include +#endif + +/* Handle DSO symbol visibility */ +#if defined _WIN32 +# if defined ZMQ_STATIC +# define ZMQ_EXPORT +# elif defined DLL_EXPORT +# define ZMQ_EXPORT __declspec(dllexport) +# else +# define ZMQ_EXPORT __declspec(dllimport) +# endif +#else +# if defined __SUNPRO_C || defined __SUNPRO_CC +# define ZMQ_EXPORT __global +# elif (defined __GNUC__ && __GNUC__ >= 4) || defined __INTEL_COMPILER +# define ZMQ_EXPORT __attribute__ ((visibility("default"))) +# else +# define ZMQ_EXPORT +# endif +#endif + +/* Define integer types needed for event interface */ +#if defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_OPENVMS +# include +#elif defined _MSC_VER && _MSC_VER < 1600 +# ifndef int32_t +typedef __int32 int32_t; +# endif +# ifndef uint16_t +typedef unsigned __int16 uint16_t; +# endif +# ifndef uint8_t +typedef unsigned __int8 uint8_t; +# endif +#else +# include +#endif + + +/******************************************************************************/ +/* 0MQ errors. */ +/******************************************************************************/ + +/* A number random enough not to collide with different errno ranges on */ +/* different OSes. The assumption is that error_t is at least 32-bit type. */ +#define ZMQ_HAUSNUMERO 156384712 + +/* On Windows platform some of the standard POSIX errnos are not defined. */ +#ifndef ENOTSUP +#define ENOTSUP (ZMQ_HAUSNUMERO + 1) +#endif +#ifndef EPROTONOSUPPORT +#define EPROTONOSUPPORT (ZMQ_HAUSNUMERO + 2) +#endif +#ifndef ENOBUFS +#define ENOBUFS (ZMQ_HAUSNUMERO + 3) +#endif +#ifndef ENETDOWN +#define ENETDOWN (ZMQ_HAUSNUMERO + 4) +#endif +#ifndef EADDRINUSE +#define EADDRINUSE (ZMQ_HAUSNUMERO + 5) +#endif +#ifndef EADDRNOTAVAIL +#define EADDRNOTAVAIL (ZMQ_HAUSNUMERO + 6) +#endif +#ifndef ECONNREFUSED +#define ECONNREFUSED (ZMQ_HAUSNUMERO + 7) +#endif +#ifndef EINPROGRESS +#define EINPROGRESS (ZMQ_HAUSNUMERO + 8) +#endif +#ifndef ENOTSOCK +#define ENOTSOCK (ZMQ_HAUSNUMERO + 9) +#endif +#ifndef EMSGSIZE +#define EMSGSIZE (ZMQ_HAUSNUMERO + 10) +#endif +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT (ZMQ_HAUSNUMERO + 11) +#endif +#ifndef ENETUNREACH +#define ENETUNREACH (ZMQ_HAUSNUMERO + 12) +#endif +#ifndef ECONNABORTED +#define ECONNABORTED (ZMQ_HAUSNUMERO + 13) +#endif +#ifndef ECONNRESET +#define ECONNRESET (ZMQ_HAUSNUMERO + 14) +#endif +#ifndef ENOTCONN +#define ENOTCONN (ZMQ_HAUSNUMERO + 15) +#endif +#ifndef ETIMEDOUT +#define ETIMEDOUT (ZMQ_HAUSNUMERO + 16) +#endif +#ifndef EHOSTUNREACH +#define EHOSTUNREACH (ZMQ_HAUSNUMERO + 17) +#endif +#ifndef ENETRESET +#define ENETRESET (ZMQ_HAUSNUMERO + 18) +#endif + +/* Native 0MQ error codes. */ +#define EFSM (ZMQ_HAUSNUMERO + 51) +#define ENOCOMPATPROTO (ZMQ_HAUSNUMERO + 52) +#define ETERM (ZMQ_HAUSNUMERO + 53) +#define EMTHREAD (ZMQ_HAUSNUMERO + 54) + +/* Run-time API version detection */ +ZMQ_EXPORT void zmq_version (int *major, int *minor, int *patch); + +/* This function retrieves the errno as it is known to 0MQ library. The goal */ +/* of this function is to make the code 100% portable, including where 0MQ */ +/* compiled with certain CRT library (on Windows) is linked to an */ +/* application that uses different CRT library. */ +ZMQ_EXPORT int zmq_errno (void); + +/* Resolves system errors and 0MQ errors to human-readable string. */ +ZMQ_EXPORT const char *zmq_strerror (int errnum); + +/******************************************************************************/ +/* 0MQ infrastructure (a.k.a. context) initialisation & termination. */ +/******************************************************************************/ + +/* New API */ +/* Context options */ +#define ZMQ_IO_THREADS 1 +#define ZMQ_MAX_SOCKETS 2 + +/* Default for new contexts */ +#define ZMQ_IO_THREADS_DFLT 1 +#define ZMQ_MAX_SOCKETS_DFLT 1023 + +ZMQ_EXPORT void *zmq_ctx_new (void); +ZMQ_EXPORT int zmq_ctx_term (void *context); +ZMQ_EXPORT int zmq_ctx_shutdown (void *ctx_); +ZMQ_EXPORT int zmq_ctx_set (void *context, int option, int optval); +ZMQ_EXPORT int zmq_ctx_get (void *context, int option); + +/* Old (legacy) API */ +ZMQ_EXPORT void *zmq_init (int io_threads); +ZMQ_EXPORT int zmq_term (void *context); +ZMQ_EXPORT int zmq_ctx_destroy (void *context); + + +/******************************************************************************/ +/* 0MQ message definition. */ +/******************************************************************************/ + +typedef struct zmq_msg_t {unsigned char _ [32];} zmq_msg_t; + +typedef void (zmq_free_fn) (void *data, void *hint); + +ZMQ_EXPORT int zmq_msg_init (zmq_msg_t *msg); +ZMQ_EXPORT int zmq_msg_init_size (zmq_msg_t *msg, size_t size); +ZMQ_EXPORT int zmq_msg_init_data (zmq_msg_t *msg, void *data, + size_t size, zmq_free_fn *ffn, void *hint); +ZMQ_EXPORT int zmq_msg_send (zmq_msg_t *msg, void *s, int flags); +ZMQ_EXPORT int zmq_msg_recv (zmq_msg_t *msg, void *s, int flags); +ZMQ_EXPORT int zmq_msg_close (zmq_msg_t *msg); +ZMQ_EXPORT int zmq_msg_move (zmq_msg_t *dest, zmq_msg_t *src); +ZMQ_EXPORT int zmq_msg_copy (zmq_msg_t *dest, zmq_msg_t *src); +ZMQ_EXPORT void *zmq_msg_data (zmq_msg_t *msg); +ZMQ_EXPORT size_t zmq_msg_size (zmq_msg_t *msg); +ZMQ_EXPORT int zmq_msg_more (zmq_msg_t *msg); +ZMQ_EXPORT int zmq_msg_get (zmq_msg_t *msg, int option); +ZMQ_EXPORT int zmq_msg_set (zmq_msg_t *msg, int option, int optval); + + +/******************************************************************************/ +/* 0MQ socket definition. */ +/******************************************************************************/ + +/* Socket types. */ +#define ZMQ_PAIR 0 +#define ZMQ_PUB 1 +#define ZMQ_SUB 2 +#define ZMQ_REQ 3 +#define ZMQ_REP 4 +#define ZMQ_DEALER 5 +#define ZMQ_ROUTER 6 +#define ZMQ_PULL 7 +#define ZMQ_PUSH 8 +#define ZMQ_XPUB 9 +#define ZMQ_XSUB 10 +#define ZMQ_STREAM 11 + +/* Deprecated aliases */ +#define ZMQ_XREQ ZMQ_DEALER +#define ZMQ_XREP ZMQ_ROUTER + +/* Socket options. */ +#define ZMQ_AFFINITY 4 +#define ZMQ_IDENTITY 5 +#define ZMQ_SUBSCRIBE 6 +#define ZMQ_UNSUBSCRIBE 7 +#define ZMQ_RATE 8 +#define ZMQ_RECOVERY_IVL 9 +#define ZMQ_SNDBUF 11 +#define ZMQ_RCVBUF 12 +#define ZMQ_RCVMORE 13 +#define ZMQ_FD 14 +#define ZMQ_EVENTS 15 +#define ZMQ_TYPE 16 +#define ZMQ_LINGER 17 +#define ZMQ_RECONNECT_IVL 18 +#define ZMQ_BACKLOG 19 +#define ZMQ_RECONNECT_IVL_MAX 21 +#define ZMQ_MAXMSGSIZE 22 +#define ZMQ_SNDHWM 23 +#define ZMQ_RCVHWM 24 +#define ZMQ_MULTICAST_HOPS 25 +#define ZMQ_RCVTIMEO 27 +#define ZMQ_SNDTIMEO 28 +#define ZMQ_LAST_ENDPOINT 32 +#define ZMQ_ROUTER_MANDATORY 33 +#define ZMQ_TCP_KEEPALIVE 34 +#define ZMQ_TCP_KEEPALIVE_CNT 35 +#define ZMQ_TCP_KEEPALIVE_IDLE 36 +#define ZMQ_TCP_KEEPALIVE_INTVL 37 +#define ZMQ_TCP_ACCEPT_FILTER 38 +#define ZMQ_IMMEDIATE 39 +#define ZMQ_XPUB_VERBOSE 40 +#define ZMQ_ROUTER_RAW 41 +#define ZMQ_IPV6 42 +#define ZMQ_MECHANISM 43 +#define ZMQ_PLAIN_SERVER 44 +#define ZMQ_PLAIN_USERNAME 45 +#define ZMQ_PLAIN_PASSWORD 46 +#define ZMQ_CURVE_SERVER 47 +#define ZMQ_CURVE_PUBLICKEY 48 +#define ZMQ_CURVE_SECRETKEY 49 +#define ZMQ_CURVE_SERVERKEY 50 +#define ZMQ_PROBE_ROUTER 51 +#define ZMQ_REQ_CORRELATE 52 +#define ZMQ_REQ_RELAXED 53 +#define ZMQ_CONFLATE 54 +#define ZMQ_ZAP_DOMAIN 55 + +/* Message options */ +#define ZMQ_MORE 1 + +/* Send/recv options. */ +#define ZMQ_DONTWAIT 1 +#define ZMQ_SNDMORE 2 + +/* Security mechanisms */ +#define ZMQ_NULL 0 +#define ZMQ_PLAIN 1 +#define ZMQ_CURVE 2 + +/* Deprecated options and aliases */ +#define ZMQ_IPV4ONLY 31 +#define ZMQ_DELAY_ATTACH_ON_CONNECT ZMQ_IMMEDIATE +#define ZMQ_NOBLOCK ZMQ_DONTWAIT +#define ZMQ_FAIL_UNROUTABLE ZMQ_ROUTER_MANDATORY +#define ZMQ_ROUTER_BEHAVIOR ZMQ_ROUTER_MANDATORY + +/******************************************************************************/ +/* 0MQ socket events and monitoring */ +/******************************************************************************/ + +/* Socket transport events (tcp and ipc only) */ +#define ZMQ_EVENT_CONNECTED 1 +#define ZMQ_EVENT_CONNECT_DELAYED 2 +#define ZMQ_EVENT_CONNECT_RETRIED 4 + +#define ZMQ_EVENT_LISTENING 8 +#define ZMQ_EVENT_BIND_FAILED 16 + +#define ZMQ_EVENT_ACCEPTED 32 +#define ZMQ_EVENT_ACCEPT_FAILED 64 + +#define ZMQ_EVENT_CLOSED 128 +#define ZMQ_EVENT_CLOSE_FAILED 256 +#define ZMQ_EVENT_DISCONNECTED 512 +#define ZMQ_EVENT_MONITOR_STOPPED 1024 + +#define ZMQ_EVENT_ALL ( ZMQ_EVENT_CONNECTED | ZMQ_EVENT_CONNECT_DELAYED | \ + ZMQ_EVENT_CONNECT_RETRIED | ZMQ_EVENT_LISTENING | \ + ZMQ_EVENT_BIND_FAILED | ZMQ_EVENT_ACCEPTED | \ + ZMQ_EVENT_ACCEPT_FAILED | ZMQ_EVENT_CLOSED | \ + ZMQ_EVENT_CLOSE_FAILED | ZMQ_EVENT_DISCONNECTED | \ + ZMQ_EVENT_MONITOR_STOPPED) + +/* Socket event data */ +typedef struct { + uint16_t event; // id of the event as bitfield + int32_t value ; // value is either error code, fd or reconnect interval +} zmq_event_t; + +ZMQ_EXPORT void *zmq_socket (void *, int type); +ZMQ_EXPORT int zmq_close (void *s); +ZMQ_EXPORT int zmq_setsockopt (void *s, int option, const void *optval, + size_t optvallen); +ZMQ_EXPORT int zmq_getsockopt (void *s, int option, void *optval, + size_t *optvallen); +ZMQ_EXPORT int zmq_bind (void *s, const char *addr); +ZMQ_EXPORT int zmq_connect (void *s, const char *addr); +ZMQ_EXPORT int zmq_unbind (void *s, const char *addr); +ZMQ_EXPORT int zmq_disconnect (void *s, const char *addr); +ZMQ_EXPORT int zmq_send (void *s, const void *buf, size_t len, int flags); +ZMQ_EXPORT int zmq_send_const (void *s, const void *buf, size_t len, int flags); +ZMQ_EXPORT int zmq_recv (void *s, void *buf, size_t len, int flags); +ZMQ_EXPORT int zmq_socket_monitor (void *s, const char *addr, int events); + +ZMQ_EXPORT int zmq_sendmsg (void *s, zmq_msg_t *msg, int flags); +ZMQ_EXPORT int zmq_recvmsg (void *s, zmq_msg_t *msg, int flags); + +/* Experimental */ +struct iovec; + +ZMQ_EXPORT int zmq_sendiov (void *s, struct iovec *iov, size_t count, int flags); +ZMQ_EXPORT int zmq_recviov (void *s, struct iovec *iov, size_t *count, int flags); + +/******************************************************************************/ +/* I/O multiplexing. */ +/******************************************************************************/ + +#define ZMQ_POLLIN 1 +#define ZMQ_POLLOUT 2 +#define ZMQ_POLLERR 4 + +typedef struct +{ + void *socket; +#if defined _WIN32 + SOCKET fd; +#else + int fd; +#endif + short events; + short revents; +} zmq_pollitem_t; + +#define ZMQ_POLLITEMS_DFLT 16 + +ZMQ_EXPORT int zmq_poll (zmq_pollitem_t *items, int nitems, long timeout); + +/* Built-in message proxy (3-way) */ + +ZMQ_EXPORT int zmq_proxy (void *frontend, void *backend, void *capture); + +/* Encode a binary key as printable text using ZMQ RFC 32 */ +ZMQ_EXPORT char *zmq_z85_encode (char *dest, uint8_t *data, size_t size); + +/* Encode a binary key from printable text per ZMQ RFC 32 */ +ZMQ_EXPORT uint8_t *zmq_z85_decode (uint8_t *dest, char *string); + +/* Deprecated aliases */ +#define ZMQ_STREAMER 1 +#define ZMQ_FORWARDER 2 +#define ZMQ_QUEUE 3 +/* Deprecated method */ +ZMQ_EXPORT int zmq_device (int type, void *frontend, void *backend); + +#undef ZMQ_EXPORT + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/Source/zeromq/include/zmq_utils.h b/Source/zeromq/include/zmq_utils.h new file mode 100644 index 0000000..9b14aa7 --- /dev/null +++ b/Source/zeromq/include/zmq_utils.h @@ -0,0 +1,105 @@ +/* + Copyright (c) 2007-2013 Contributors as noted in the AUTHORS file + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef __ZMQ_UTILS_H_INCLUDED__ +#define __ZMQ_UTILS_H_INCLUDED__ + +#include +#include +#include + +/* Define integer types needed for event interface */ +#if defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_OPENVMS +# include +#elif defined _MSC_VER && _MSC_VER < 1600 +# ifndef int32_t +typedef __int32 int32_t; +# endif +# ifndef uint16_t +typedef unsigned __int16 uint16_t; +# endif +#else +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Handle DSO symbol visibility */ +#if defined _WIN32 +# if defined ZMQ_STATIC +# define ZMQ_EXPORT +# elif defined DLL_EXPORT +# define ZMQ_EXPORT __declspec(dllexport) +# else +# define ZMQ_EXPORT __declspec(dllimport) +# endif +#else +# if defined __SUNPRO_C || defined __SUNPRO_CC +# define ZMQ_EXPORT __global +# elif (defined __GNUC__ && __GNUC__ >= 4) || defined __INTEL_COMPILER +# define ZMQ_EXPORT __attribute__ ((visibility("default"))) +# else +# define ZMQ_EXPORT +# endif +#endif + +/* These functions are documented by man pages */ + +/* Encode data with Z85 encoding. Returns encoded data */ +ZMQ_EXPORT char *zmq_z85_encode (char *dest, uint8_t *data, size_t size); + +/* Decode data with Z85 encoding. Returns decoded data */ +ZMQ_EXPORT uint8_t *zmq_z85_decode (uint8_t *dest, char *string); + +/* Generate z85-encoded public and private keypair with libsodium. */ +/* Returns 0 on success. */ +ZMQ_EXPORT int zmq_curve_keypair (char *z85_public_key, char *z85_secret_key); + +typedef void (zmq_thread_fn) (void*); + +/* These functions are not documented by man pages */ + +/* Helper functions are used by perf tests so that they don't have to care */ +/* about minutiae of time-related functions on different OS platforms. */ + +/* Starts the stopwatch. Returns the handle to the watch. */ +ZMQ_EXPORT void *zmq_stopwatch_start (void); + +/* Stops the stopwatch. Returns the number of microseconds elapsed since */ +/* the stopwatch was started. */ +ZMQ_EXPORT unsigned long zmq_stopwatch_stop (void *watch_); + +/* Sleeps for specified number of seconds. */ +ZMQ_EXPORT void zmq_sleep (int seconds_); + +/* Start a thread. Returns a handle to the thread. */ +ZMQ_EXPORT void *zmq_threadstart (zmq_thread_fn* func, void* arg); + +/* Wait for thread to complete then free up resources. */ +ZMQ_EXPORT void zmq_threadclose (void* thread); + +#undef ZMQ_EXPORT + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Source/zmq.hpp b/Source/zmq.hpp new file mode 100644 index 0000000..147f096 --- /dev/null +++ b/Source/zmq.hpp @@ -0,0 +1,566 @@ +/* + Copyright (c) 2009-2011 250bpm s.r.o. + Copyright (c) 2011 Botond Ballo + Copyright (c) 2007-2009 iMatix Corporation + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef __ZMQ_HPP_INCLUDED__ +#define __ZMQ_HPP_INCLUDED__ + +#include + +#include +#include +#include +#include +#include + +// Detect whether the compiler supports C++11 rvalue references. +#if (defined(__GNUC__) && (__GNUC__ > 4 || \ + (__GNUC__ == 4 && __GNUC_MINOR__ > 2)) && \ + defined(__GXX_EXPERIMENTAL_CXX0X__)) + #define ZMQ_HAS_RVALUE_REFS + #define ZMQ_DELETED_FUNCTION = delete +#elif defined(__clang__) + #if __has_feature(cxx_rvalue_references) + #define ZMQ_HAS_RVALUE_REFS + #endif + + #if __has_feature(cxx_deleted_functions) + #define ZMQ_DELETED_FUNCTION = delete + #else + #define ZMQ_DELETED_FUNCTION + #endif +#elif defined(_MSC_VER) && (_MSC_VER >= 1600) + #define ZMQ_HAS_RVALUE_REFS + #define ZMQ_DELETED_FUNCTION +#else + #define ZMQ_DELETED_FUNCTION +#endif + +#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3, 3, 0) +#define ZMQ_NEW_MONITOR_EVENT_LAYOUT +#endif + +// In order to prevent unused variable warnings when building in non-debug +// mode use this macro to make assertions. +#ifndef NDEBUG +# define ZMQ_ASSERT(expression) assert(expression) +#else +# define ZMQ_ASSERT(expression) (void)(expression) +#endif + +namespace zmq +{ + + typedef zmq_free_fn free_fn; + typedef zmq_pollitem_t pollitem_t; + + class error_t : public std::exception + { + public: + + error_t () : errnum (zmq_errno ()) {} + + virtual const char *what () const throw () + { + return zmq_strerror (errnum); + } + + int num () const + { + return errnum; + } + + private: + + int errnum; + }; + + inline int poll (zmq_pollitem_t *items_, int nitems_, long timeout_ = -1) + { + int rc = zmq_poll (items_, nitems_, timeout_); + if (rc < 0) + throw error_t (); + return rc; + } + + inline void proxy (void *frontend, void *backend, void *capture) + { + int rc = zmq_proxy (frontend, backend, capture); + if (rc != 0) + throw error_t (); + } + + inline void version (int *major_, int *minor_, int *patch_) + { + zmq_version (major_, minor_, patch_); + } + + class message_t + { + friend class socket_t; + + public: + + inline message_t () + { + int rc = zmq_msg_init (&msg); + if (rc != 0) + throw error_t (); + } + + inline explicit message_t (size_t size_) + { + int rc = zmq_msg_init_size (&msg, size_); + if (rc != 0) + throw error_t (); + } + + inline message_t (void *data_, size_t size_, free_fn *ffn_, + void *hint_ = NULL) + { + int rc = zmq_msg_init_data (&msg, data_, size_, ffn_, hint_); + if (rc != 0) + throw error_t (); + } + +#ifdef ZMQ_HAS_RVALUE_REFS + inline message_t (message_t &&rhs) : msg (rhs.msg) + { + int rc = zmq_msg_init (&rhs.msg); + if (rc != 0) + throw error_t (); + } + + inline message_t &operator = (message_t &&rhs) + { + std::swap (msg, rhs.msg); + return *this; + } +#endif + + inline ~message_t () + { + int rc = zmq_msg_close (&msg); + ZMQ_ASSERT (rc == 0); + } + + inline void rebuild () + { + int rc = zmq_msg_close (&msg); + if (rc != 0) + throw error_t (); + rc = zmq_msg_init (&msg); + if (rc != 0) + throw error_t (); + } + + inline void rebuild (size_t size_) + { + int rc = zmq_msg_close (&msg); + if (rc != 0) + throw error_t (); + rc = zmq_msg_init_size (&msg, size_); + if (rc != 0) + throw error_t (); + } + + inline void rebuild (void *data_, size_t size_, free_fn *ffn_, + void *hint_ = NULL) + { + int rc = zmq_msg_close (&msg); + if (rc != 0) + throw error_t (); + rc = zmq_msg_init_data (&msg, data_, size_, ffn_, hint_); + if (rc != 0) + throw error_t (); + } + + inline void move (message_t *msg_) + { + int rc = zmq_msg_move (&msg, &(msg_->msg)); + if (rc != 0) + throw error_t (); + } + + inline void copy (message_t *msg_) + { + int rc = zmq_msg_copy (&msg, &(msg_->msg)); + if (rc != 0) + throw error_t (); + } + + inline bool more () + { + int rc = zmq_msg_more (&msg); + return rc != 0; + } + + inline void *data () + { + return zmq_msg_data (&msg); + } + + inline const void* data () const + { + return zmq_msg_data (const_cast(&msg)); + } + + inline size_t size () const + { + return zmq_msg_size (const_cast(&msg)); + } + + private: + + // The underlying message + zmq_msg_t msg; + + // Disable implicit message copying, so that users won't use shared + // messages (less efficient) without being aware of the fact. + message_t (const message_t&); + void operator = (const message_t&); + }; + + class context_t + { + friend class socket_t; + + public: + inline context_t () + { + ptr = zmq_ctx_new (); + if (ptr == NULL) + throw error_t (); + } + + + inline explicit context_t (int io_threads_) + { + ptr = zmq_ctx_new (); + if (ptr == NULL) + throw error_t (); + + int rc = zmq_ctx_set (ptr, ZMQ_IO_THREADS, io_threads_); + ZMQ_ASSERT (rc == 0); + } + +#ifdef ZMQ_HAS_RVALUE_REFS + inline context_t (context_t &&rhs) : ptr (rhs.ptr) + { + rhs.ptr = NULL; + } + inline context_t &operator = (context_t &&rhs) + { + std::swap (ptr, rhs.ptr); + return *this; + } +#endif + + inline ~context_t () + { + close(); + } + + inline void close() + { + if (ptr == NULL) + return; + int rc = zmq_ctx_destroy (ptr); + ZMQ_ASSERT (rc == 0); + ptr = NULL; + } + + // Be careful with this, it's probably only useful for + // using the C api together with an existing C++ api. + // Normally you should never need to use this. + inline operator void* () + { + return ptr; + } + + private: + + void *ptr; + + context_t (const context_t&); + void operator = (const context_t&); + }; + + class socket_t + { + friend class monitor_t; + public: + + inline socket_t (context_t &context_, int type_) + { + ctxptr = context_.ptr; + ptr = zmq_socket (context_.ptr, type_); + if (ptr == NULL) + throw error_t (); + } + +#ifdef ZMQ_HAS_RVALUE_REFS + inline socket_t(socket_t&& rhs) : ptr(rhs.ptr) + { + rhs.ptr = NULL; + } + inline socket_t& operator=(socket_t&& rhs) + { + std::swap(ptr, rhs.ptr); + return *this; + } +#endif + + inline ~socket_t () + { + close(); + } + + inline operator void* () + { + return ptr; + } + + inline void close() + { + if(ptr == NULL) + // already closed + return ; + int rc = zmq_close (ptr); + ZMQ_ASSERT (rc == 0); + ptr = 0 ; + } + + inline void setsockopt (int option_, const void *optval_, + size_t optvallen_) + { + int rc = zmq_setsockopt (ptr, option_, optval_, optvallen_); + if (rc != 0) + throw error_t (); + } + + inline void getsockopt (int option_, void *optval_, + size_t *optvallen_) + { + int rc = zmq_getsockopt (ptr, option_, optval_, optvallen_); + if (rc != 0) + throw error_t (); + } + + inline void bind (const char *addr_) + { + int rc = zmq_bind (ptr, addr_); + if (rc != 0) + throw error_t (); + } + + inline void unbind (const char *addr_) + { + int rc = zmq_unbind (ptr, addr_); + if (rc != 0) + throw error_t (); + } + + inline void connect (const char *addr_) + { + int rc = zmq_connect (ptr, addr_); + if (rc != 0) + throw error_t (); + } + + inline void disconnect (const char *addr_) + { + int rc = zmq_disconnect (ptr, addr_); + if (rc != 0) + throw error_t (); + } + + inline bool connected() + { + return(ptr != NULL); + } + + inline size_t send (const void *buf_, size_t len_, int flags_ = 0) + { + int nbytes = zmq_send (ptr, buf_, len_, flags_); + if (nbytes >= 0) + return (size_t) nbytes; + if (zmq_errno () == EAGAIN) + return 0; + throw error_t (); + } + + inline bool send (message_t &msg_, int flags_ = 0) + { + int nbytes = zmq_msg_send (&(msg_.msg), ptr, flags_); + if (nbytes >= 0) + return true; + if (zmq_errno () == EAGAIN) + return false; + throw error_t (); + } + + inline size_t recv (void *buf_, size_t len_, int flags_ = 0) + { + int nbytes = zmq_recv (ptr, buf_, len_, flags_); + if (nbytes >= 0) + return (size_t) nbytes; + if (zmq_errno () == EAGAIN) + return 0; + throw error_t (); + } + + inline bool recv (message_t *msg_, int flags_ = 0) + { + int nbytes = zmq_msg_recv (&(msg_->msg), ptr, flags_); + if (nbytes >= 0) + return true; + if (zmq_errno () == EAGAIN) + return false; + throw error_t (); + } + + private: + void *ptr; + void *ctxptr; + + socket_t (const socket_t&) ZMQ_DELETED_FUNCTION; + void operator = (const socket_t&) ZMQ_DELETED_FUNCTION; + }; + + class monitor_t + { + public: + monitor_t() : socketPtr(NULL) {} + virtual ~monitor_t() {} + + void monitor(socket_t &socket, const char *addr_, int events = ZMQ_EVENT_ALL) + { + int rc = zmq_socket_monitor(socket.ptr, addr_, events); + if (rc != 0) + throw error_t (); + + socketPtr = socket.ptr; + void *s = zmq_socket (socket.ctxptr, ZMQ_PAIR); + assert (s); + + rc = zmq_connect (s, addr_); + assert (rc == 0); + + on_monitor_started(); + + while (true) { + zmq_msg_t eventMsg; + zmq_msg_init (&eventMsg); + rc = zmq_recvmsg (s, &eventMsg, 0); + if (rc == -1 && zmq_errno() == ETERM) + break; + assert (rc != -1); + zmq_event_t* event = static_cast(zmq_msg_data (&eventMsg)); + +#ifdef ZMQ_NEW_MONITOR_EVENT_LAYOUT + zmq_msg_t addrMsg; + zmq_msg_init (&addrMsg); + rc = zmq_recvmsg (s, &addrMsg, 0); + if (rc == -1 && zmq_errno() == ETERM) + break; + assert (rc != -1); + const char* str = static_cast(zmq_msg_data (&addrMsg)); + std::string address(str, str + zmq_msg_size(&addrMsg)); + zmq_msg_close (&addrMsg); +#else + // Bit of a hack, but all events in the zmq_event_t union have the same layout so this will work for all event types. + std::string address = event->data.connected.addr; +#endif + +#ifdef ZMQ_EVENT_MONITOR_STOPPED + if (event->event == ZMQ_EVENT_MONITOR_STOPPED) + break; +#endif + + switch (event->event) { + case ZMQ_EVENT_CONNECTED: + on_event_connected(*event, address.c_str()); + break; + case ZMQ_EVENT_CONNECT_DELAYED: + on_event_connect_delayed(*event, address.c_str()); + break; + case ZMQ_EVENT_CONNECT_RETRIED: + on_event_connect_retried(*event, address.c_str()); + break; + case ZMQ_EVENT_LISTENING: + on_event_listening(*event, address.c_str()); + break; + case ZMQ_EVENT_BIND_FAILED: + on_event_bind_failed(*event, address.c_str()); + break; + case ZMQ_EVENT_ACCEPTED: + on_event_accepted(*event, address.c_str()); + break; + case ZMQ_EVENT_ACCEPT_FAILED: + on_event_accept_failed(*event, address.c_str()); + break; + case ZMQ_EVENT_CLOSED: + on_event_closed(*event, address.c_str()); + break; + case ZMQ_EVENT_CLOSE_FAILED: + on_event_close_failed(*event, address.c_str()); + break; + case ZMQ_EVENT_DISCONNECTED: + on_event_disconnected(*event, address.c_str()); + break; + default: + on_event_unknown(*event, address.c_str()); + break; + } + zmq_msg_close (&eventMsg); + } + zmq_close (s); + socketPtr = NULL; + } + +#ifdef ZMQ_EVENT_MONITOR_STOPPED + void abort() + { + if (socketPtr) + zmq_socket_monitor(socketPtr, NULL, 0); + } +#endif + virtual void on_monitor_started() {} + virtual void on_event_connected(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + virtual void on_event_connect_delayed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + virtual void on_event_connect_retried(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + virtual void on_event_listening(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + virtual void on_event_bind_failed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + virtual void on_event_accepted(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + virtual void on_event_accept_failed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + virtual void on_event_closed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + virtual void on_event_close_failed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + virtual void on_event_disconnected(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + virtual void on_event_unknown(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + private: + void* socketPtr; + }; +} + +#endif