open_toontown_panda3d/pandatool/src/pstatserver/pStatServer.cxx

294 lines
7.4 KiB
C++

/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file pStatServer.cxx
* @author drose
* @date 2000-07-09
*/
#include "pStatServer.h"
#include "pStatReader.h"
#include "thread.h"
#include "config_pstatclient.h"
/**
*
*/
PStatServer::
PStatServer() {
_listener = new PStatListener(this);
_next_udp_port = 0;
}
/**
*
*/
PStatServer::
~PStatServer() {
delete _listener;
}
/**
* Establishes a port number that the manager will listen on for TCP
* connections. This may be called more than once to listen simulataneously
* on multiple connections, as if that were at all useful.
*
* The default parameter, -1, indicates the use of whatever port number has
* been indicated in the Config file.
*
* This function returns true if the port was successfully opened, or false if
* it could not open the port.
*/
bool PStatServer::
listen(int port) {
if (port < 0) {
port = pstats_port;
}
// Now try to listen to the port.
PT(Connection) rendezvous = open_TCP_server_rendezvous(port, 5);
if (rendezvous.is_null()) {
// Couldn't get it.
return false;
}
// Tell the listener about the new port.
_listener->add_connection(rendezvous);
if (_next_udp_port == 0) {
_next_udp_port = port + 1;
}
return true;
}
/**
* Checks for any network activity and handles it, if appropriate, and then
* returns. This must be called periodically unless is_thread_safe() is
* redefined to return true on this class and also on all PStatMonitors in
* use.
*
* Alternatively, a program may call main_loop() and yield control of the
* program entirely to the PStatServer.
*/
void PStatServer::
poll() {
// Delete all the readers that we couldn't delete before.
while (!_lost_readers.empty()) {
PStatReader *reader = _lost_readers.back();
_lost_readers.pop_back();
reader->lost_connection();
delete reader;
}
while (!_removed_readers.empty()) {
PStatReader *reader = _removed_readers.back();
_removed_readers.pop_back();
delete reader;
}
_listener->poll();
Readers::const_iterator ri = _readers.begin();
while (ri != _readers.end()) {
// Preincrement the iterator, in case we remove it as a result of calling
// poll().
Readers::const_iterator rnext = ri;
++rnext;
PStatReader *reader = (*ri).second;
reader->poll();
reader->idle();
ri = rnext;
}
}
/**
* An alternative to repeatedly calling poll(), this function yields control
* of the program to the PStatServer. It does not return until the program is
* done.
*
* If interrupt_flag is non-NULL, it is the address of a bool variable that is
* initially false, and may be asynchronously set true to indicate the loop
* should terminate.
*/
void PStatServer::
main_loop(bool *interrupt_flag) {
while (interrupt_flag == nullptr || !*interrupt_flag) {
poll();
Thread::sleep(0.1);
}
}
/**
* Adds the newly-created PStatReader to the list of currently active readers.
*/
void PStatServer::
add_reader(Connection *connection, PStatReader *reader) {
_readers[connection] = reader;
}
/**
* Removes the indicated reader.
*/
void PStatServer::
remove_reader(Connection *connection, PStatReader *reader) {
Readers::iterator ri;
ri = _readers.find(connection);
if (ri == _readers.end() || (*ri).second != reader) {
nout << "Attempt to remove undefined reader.\n";
} else {
_readers.erase(ri);
_removed_readers.push_back(reader);
}
}
/**
* Returns a new port number that will probably be free to use as a UDP port.
* The caller should be prepared to accept the possibility that it will be
* already in use by another process, however.
*/
int PStatServer::
get_udp_port() {
if (_available_udp_ports.empty()) {
return _next_udp_port++;
}
int udp_port = _available_udp_ports.front();
_available_udp_ports.pop_front();
return udp_port;
}
/**
* Indicates that the given UDP port is once again free for use.
*/
void PStatServer::
release_udp_port(int port) {
_available_udp_ports.push_back(port);
}
/**
* Returns the current number of user-defined guide bars.
*/
int PStatServer::
get_num_user_guide_bars() const {
return _user_guide_bars.size();
}
/**
* Returns the height of the nth user-defined guide bar.
*/
double PStatServer::
get_user_guide_bar_height(int n) const {
nassertr(n >= 0 && n < (int)_user_guide_bars.size(), 0.0f);
return _user_guide_bars[n];
}
/**
* Adjusts the height of the nth user-defined guide bar.
*/
void PStatServer::
move_user_guide_bar(int n, double height) {
nassertv(n >= 0 && n < (int)_user_guide_bars.size());
_user_guide_bars[n] = height;
user_guide_bars_changed();
}
/**
* Creates a new user guide bar and returns its index number.
*/
int PStatServer::
add_user_guide_bar(double height) {
int n = (int)_user_guide_bars.size();
_user_guide_bars.push_back(height);
user_guide_bars_changed();
return n;
}
/**
* Removes the user guide bar with the indicated index number. All subsequent
* index numbers are adjusted down one.
*/
void PStatServer::
remove_user_guide_bar(int n) {
nassertv(n >= 0 && n < (int)_user_guide_bars.size());
_user_guide_bars.erase(_user_guide_bars.begin() + n);
user_guide_bars_changed();
}
/**
* Returns the index number of the first user guide bar found whose height is
* within the indicated range, or -1 if no user guide bars fall within the
* range.
*/
int PStatServer::
find_user_guide_bar(double from_height, double to_height) const {
GuideBars::const_iterator gbi;
for (gbi = _user_guide_bars.begin();
gbi != _user_guide_bars.end();
++gbi) {
double height = (*gbi);
if (height >= from_height && height <= to_height) {
return (int)(gbi - _user_guide_bars.begin());
}
}
return -1;
}
/**
* Called when the user guide bars have been changed.
*/
void PStatServer::
user_guide_bars_changed() {
Readers::iterator ri;
for (ri = _readers.begin(); ri != _readers.end(); ++ri) {
(*ri).second->get_monitor()->user_guide_bars_changed();
}
}
/**
* This should be redefined to return true in derived classes that want to
* deal with multithreaded readers and such. If this returns true, the
* manager will create the listener in its own thread, and thus the
* PStatReader constructors at least will run in a different thread.
*
* This is not related to the question of whether the reader can handle
* multiple different PStatThreadDatas; it's strictly a question of whether
* the readers themselves can run in a separate thread.
*/
bool PStatServer::
is_thread_safe() {
return false;
}
/**
* Called when a lost connection is detected by the net code, this should pass
* the word on to the interested parties and clean up gracefully.
*/
void PStatServer::
connection_reset(const PT(Connection) &connection, bool okflag) {
// Was this a client connection? Tell the reader about it if it was.
close_connection(connection);
Readers::iterator ri;
ri = _readers.find(connection);
if (ri != _readers.end()) {
PStatReader *reader = (*ri).second;
_readers.erase(ri);
// Unfortunately, we can't delete the reader right away, because we might
// have been called from a method on the reader! We'll have to save the
// reader pointer and delete it some time later.
_lost_readers.push_back(reader);
}
}