SLikeNet  0.1.3
 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 
17 #if _RAKNET_SUPPORT_ConsoleServer==1
18 
19 #include "slikenet/ConsoleServer.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 
35 
37 {
38  transport=0;
39  password[0]=0;
40  prompt=0;
41 }
43 {
44  if (prompt)
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  {
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
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 }
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  {
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;
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  {
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");
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.
192  for (i=0; i < commandParserList.Size(); i++)
193  {
194  if (commandParserList[i]->GetRegisteredCommand(parameterList[1], &rc2))
195  {
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");
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;
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 
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 
295 
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)
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_*