SLikeNet 0.1.3

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ConsoleServer.cpp
Go to the documentation of this file.
1 /*
2 * Original work: Copyright (c) 2014, Oculus VR, Inc.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * RakNet License.txt file in the licenses directory of this source tree. An additional grant
7 * of patent rights can be found in the RakNet Patents.txt file in the same directory.
8 *
9 *
10 * Modified work: Copyright (c) 2016-2017, SLikeSoft UG (haftungsbeschränkt)
11 *
12 * This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style
13 * license found in the license.txt file in the root directory of this source tree.
14 */
15
16 #include "slikenet/NativeFeatureIncludes.h"
17 #if _RAKNET_SUPPORT_ConsoleServer==1
18
19 #include "slikenet/ConsoleServer.h"
20 #include "slikenet/TransportInterface.h"
21 #include "slikenet/CommandParserInterface.h"
22 #include <string.h>
23 #include <stdlib.h>
24
25 #define COMMAND_DELINATOR ' '
26 #define COMMAND_DELINATOR_TOGGLE '"'
27
28 #include "slikenet/LinuxStrings.h"
29 #include "slikenet/linux_adapter.h"
30 #include "slikenet/osx_adapter.h"
31
32 using namespace SLNet;
33
34 STATIC_FACTORY_DEFINITIONS(ConsoleServer,ConsoleServer);
35
36 ConsoleServer::ConsoleServer()
37 {
38 transport=0;
39 password[0]=0;
40 prompt=0;
41 }
42 ConsoleServer::~ConsoleServer()
43 {
44 if (prompt)
45 rakFree_Ex(prompt, _FILE_AND_LINE_);
46 }
47 void ConsoleServer::SetTransportProvider(TransportInterface *transportInterface, unsigned short port)
48 {
49 // Replace the current TransportInterface, stopping the old one, if present, and starting the new one.
50 if (transportInterface)
51 {
52 if (transport)
53 {
54 RemoveCommandParser(transport->GetCommandParser());
55 transport->Stop();
56 }
57 transport=transportInterface;
58 transport->Start(port, true);
59
60 unsigned i;
61 for (i=0; i < commandParserList.Size(); i++)
62 commandParserList[i]->OnTransportChange(transport);
63
64 // The transport itself might have a command parser - for example password for the RakNet transport
65 AddCommandParser(transport->GetCommandParser());
66 }
67 }
68 void ConsoleServer::AddCommandParser(CommandParserInterface *commandParserInterface)
69 {
70 if (commandParserInterface==0)
71 return;
72
73 // Non-duplicate insertion
74 unsigned i;
75 for (i=0; i < commandParserList.Size(); i++)
76 {
77 if (commandParserList[i]==commandParserInterface)
78 return;
79
80 if (_stricmp(commandParserList[i]->GetName(), commandParserInterface->GetName())==0)
81 {
82 // Naming conflict between two command parsers
83 RakAssert(0);
84 return;
85 }
86 }
87
88 commandParserList.Insert(commandParserInterface, _FILE_AND_LINE_);
89 if (transport)
90 commandParserInterface->OnTransportChange(transport);
91 }
92 void ConsoleServer::RemoveCommandParser(CommandParserInterface *commandParserInterface)
93 {
94 if (commandParserInterface==0)
95 return;
96
97 // Overwrite the element we are removing from the back of the list and delete the back of the list
98 unsigned i;
99 for (i=0; i < commandParserList.Size(); i++)
100 {
101 if (commandParserList[i]==commandParserInterface)
102 {
103 commandParserList[i]=commandParserList[commandParserList.Size()-1];
104 commandParserList.RemoveFromEnd();
105 return;
106 }
107 }
108 }
109 void ConsoleServer::Update(void)
110 {
111 unsigned i;
112 char *parameterList[20]; // Up to 20 parameters
113 unsigned numParameters;
114 SLNet::SystemAddress newOrLostConnectionId;
115 SLNet::Packet *p;
116 SLNet::RegisteredCommand rc;
117
118 p = transport->Receive();
119 newOrLostConnectionId=transport->HasNewIncomingConnection();
120
121 if (newOrLostConnectionId!=UNASSIGNED_SYSTEM_ADDRESS)
122 {
123 for (i=0; i < commandParserList.Size(); i++)
124 {
125 commandParserList[i]->OnNewIncomingConnection(newOrLostConnectionId, transport);
126 }
127
128 transport->Send(newOrLostConnectionId, "Connected to remote command console.\r\nType 'help' for help.\r\n");
129 ListParsers(newOrLostConnectionId);
130 ShowPrompt(newOrLostConnectionId);
131 }
132
133 newOrLostConnectionId=transport->HasLostConnection();
134 if (newOrLostConnectionId!=UNASSIGNED_SYSTEM_ADDRESS)
135 {
136 for (i=0; i < commandParserList.Size(); i++)
137 commandParserList[i]->OnConnectionLost(newOrLostConnectionId, transport);
138 }
139
140 while (p)
141 {
142 bool commandParsed=false;
143 char copy[REMOTE_MAX_TEXT_INPUT];
144 memcpy(copy, p->data, p->length);
145 copy[p->length]=0;
146 SLNet::CommandParserInterface::ParseConsoleString((char*)p->data, COMMAND_DELINATOR, COMMAND_DELINATOR_TOGGLE, &numParameters, parameterList, 20); // Up to 20 parameters
147 if (numParameters==0)
148 {
149 transport->DeallocatePacket(p);
150 p = transport->Receive();
151 continue;
152 }
153 if (_stricmp(*parameterList, "help")==0 && numParameters<=2)
154 {
155 // Find the parser specified and display help for it
156 if (numParameters==1)
157 {
158 transport->Send(p->systemAddress, "\r\nINSTRUCTIONS:\r\n");
159 transport->Send(p->systemAddress, "Enter commands on your keyboard, using spaces to delineate parameters.\r\n");
160 transport->Send(p->systemAddress, "You can use quotation marks to toggle space delineation.\r\n");
161 transport->Send(p->systemAddress, "You can connect multiple times from the same computer.\r\n");
162 transport->Send(p->systemAddress, "You can direct commands to a parser by prefixing the parser name or number.\r\n");
163 transport->Send(p->systemAddress, "COMMANDS:\r\n");
164 transport->Send(p->systemAddress, "help Show this display.\r\n");
165 transport->Send(p->systemAddress, "help <ParserName> Show help on a particular parser.\r\n");
166 transport->Send(p->systemAddress, "help <CommandName> Show help on a particular command.\r\n");
167 transport->Send(p->systemAddress, "quit Disconnects from the server.\r\n");
168 transport->Send(p->systemAddress, "[<ParserName>] <Command> [<Parameters>] Execute a command\r\n");
169 transport->Send(p->systemAddress, "[<ParserNumber>] <Command> [<Parameters>] Execute a command\r\n");
170 ListParsers(p->systemAddress);
171 //ShowPrompt(p->systemAddress);
172 }
173 else // numParameters == 2, including the help tag
174 {
175 for (i=0; i < commandParserList.Size(); i++)
176 {
177 if (_stricmp(parameterList[1], commandParserList[i]->GetName())==0)
178 {
179 commandParsed=true;
180 commandParserList[i]->SendHelp(transport, p->systemAddress);
181 transport->Send(p->systemAddress, "COMMAND LIST:\r\n");
182 commandParserList[i]->SendCommandList(transport, p->systemAddress);
183 transport->Send(p->systemAddress, "\r\n");
184 break;
185 }
186 }
187
188 if (commandParsed==false)
189 {
190 // Try again, for all commands for all parsers.
191 SLNet::RegisteredCommand rc2;
192 for (i=0; i < commandParserList.Size(); i++)
193 {
194 if (commandParserList[i]->GetRegisteredCommand(parameterList[1], &rc2))
195 {
196 if (rc2.parameterCount== SLNet::CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS)
197 transport->Send(p->systemAddress, "(Variable parms): %s %s\r\n", rc2.command, rc2.commandHelp);
198 else
199 transport->Send(p->systemAddress, "(%i parms): %s %s\r\n", rc2.parameterCount, rc2.command, rc2.commandHelp);
200 commandParsed=true;
201 break;
202 }
203 }
204 }
205
206 if (commandParsed==false)
207 {
208 // Don't know what to do
209 transport->Send(p->systemAddress, "Unknown help topic: %s.\r\n", parameterList[1]);
210 }
211 //ShowPrompt(p->systemAddress);
212 }
213 }
214 else if (_stricmp(*parameterList, "quit")==0 && numParameters==1)
215 {
216 transport->Send(p->systemAddress, "Goodbye!\r\n");
217 transport->CloseConnection(p->systemAddress);
218 }
219 else
220 {
221 bool tryAllParsers=true;
222 bool failed=false;
223
224 if (numParameters >=2) // At minimum <CommandParserName> <Command>
225 {
226 unsigned commandParserIndex=(unsigned)-1;
227 // Prefixing with numbers directs to a particular parser
228 if (**parameterList>='0' && **parameterList<='9')
229 {
230 commandParserIndex=atoi(*parameterList); // Use specified parser unless it's an invalid number
231 commandParserIndex--; // Subtract 1 since we displayed numbers starting at index+1
232 if (commandParserIndex >= commandParserList.Size())
233 {
234 transport->Send(p->systemAddress, "Invalid index.\r\n");
235 failed=true;
236 }
237 }
238 else
239 {
240 // // Prefixing with the name of a command parser directs to that parser. See if the first word matches a parser
241 for (i=0; i < commandParserList.Size(); i++)
242 {
243 if (_stricmp(parameterList[0], commandParserList[i]->GetName())==0)
244 {
245 commandParserIndex=i; // Matches parser at index i
246 break;
247 }
248 }
249 }
250
251 if (failed==false)
252 {
253 // -1 means undirected, so otherwise this is directed to a target
254 if (commandParserIndex!=(unsigned)-1)
255 {
256 // Only this parser should use this command
257 tryAllParsers=false;
258 if (commandParserList[commandParserIndex]->GetRegisteredCommand(parameterList[1], &rc))
259 {
260 commandParsed=true;
261 if (rc.parameterCount==CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS || rc.parameterCount==numParameters-2)
262 commandParserList[commandParserIndex]->OnCommand(rc.command, numParameters-2, parameterList+2, transport, p->systemAddress, copy);
263 else
264 transport->Send(p->systemAddress, "Invalid parameter count.\r\n(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp);
265 }
266 }
267 }
268 }
269
270 if (failed == false && tryAllParsers)
271 {
272 for (i=0; i < commandParserList.Size(); i++)
273 {
274 // Undirected command. Try all the parsers to see if they understand the command
275 // Pass the 1nd element as the command, and the remainder as the parameter list
276 if (commandParserList[i]->GetRegisteredCommand(parameterList[0], &rc))
277 {
278 commandParsed=true;
279
280 if (rc.parameterCount==CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS || rc.parameterCount==numParameters-1)
281 commandParserList[i]->OnCommand(rc.command, numParameters-1, parameterList+1, transport, p->systemAddress, copy);
282 else
283 transport->Send(p->systemAddress, "Invalid parameter count.\r\n(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp);
284 }
285 }
286 }
287 if (commandParsed==false && commandParserList.Size() > 0)
288 {
289 transport->Send(p->systemAddress, "Unknown command: Type 'help' for help.\r\n");
290 }
291
292 }
293
294 ShowPrompt(p->systemAddress);
295
296 transport->DeallocatePacket(p);
297 p = transport->Receive();
298 }
299 }
300
301 void ConsoleServer::ListParsers(SystemAddress systemAddress)
302 {
303 transport->Send(systemAddress,"INSTALLED PARSERS:\r\n");
304 unsigned i;
305 for (i=0; i < commandParserList.Size(); i++)
306 {
307 transport->Send(systemAddress, "%i. %s\r\n", i+1, commandParserList[i]->GetName());
308 }
309 }
310 void ConsoleServer::ShowPrompt(SystemAddress systemAddress)
311 {
312 transport->Send(systemAddress, prompt);
313 }
314 void ConsoleServer::SetPrompt(const char *_prompt)
315 {
316 if (prompt)
317 rakFree_Ex(prompt,_FILE_AND_LINE_);
318 if (_prompt && _prompt[0])
319 {
320 size_t len = strlen(_prompt);
321 prompt = (char*) rakMalloc_Ex(len+1,_FILE_AND_LINE_);
322 strcpy_s(prompt,len+1,_prompt);
323 }
324 else
325 prompt=0;
326 }
327
328 #endif // _RAKNET_SUPPORT_*