349 lines
12 KiB
C++
349 lines
12 KiB
C++
// Filename: pStatServer.cxx
|
|
// Created by: drose (09Jul00)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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."
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "pStatServer.h"
|
|
#include "pStatReader.h"
|
|
#include "thread.h"
|
|
#include "config_pstats.h"
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PStatServer::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
PStatServer::
|
|
PStatServer() {
|
|
_listener = new PStatListener(this);
|
|
_next_udp_port = 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PStatServer::Destructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
PStatServer::
|
|
~PStatServer() {
|
|
delete _listener;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PStatServer::listen
|
|
// Access: Public
|
|
// Description: 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;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PStatServer::poll
|
|
// Access: Public
|
|
// Description: 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;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PStatServer::main_loop
|
|
// Access: Public
|
|
// Description: 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 == (bool *)NULL || !*interrupt_flag) {
|
|
poll();
|
|
Thread::sleep(0.1);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PStatServer::add_reader
|
|
// Access: Public
|
|
// Description: Adds the newly-created PStatReader to the list of
|
|
// currently active readers.
|
|
////////////////////////////////////////////////////////////////////
|
|
void PStatServer::
|
|
add_reader(Connection *connection, PStatReader *reader) {
|
|
_readers[connection] = reader;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PStatServer::remove_reader
|
|
// Access: Public
|
|
// Description: 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);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PStatServer::get_udp_port
|
|
// Access: Public
|
|
// Description: 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;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PStatServer::release_udp_port
|
|
// Access: Public
|
|
// Description: 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);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PStatServer::get_num_user_guide_bars
|
|
// Access: Public
|
|
// Description: Returns the current number of user-defined guide
|
|
// bars.
|
|
////////////////////////////////////////////////////////////////////
|
|
int PStatServer::
|
|
get_num_user_guide_bars() const {
|
|
return _user_guide_bars.size();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PStatServer::get_user_guide_bar_height
|
|
// Access: Public
|
|
// Description: 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];
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PStatServer::move_user_guide_bar
|
|
// Access: Public
|
|
// Description: 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();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PStatServer::add_user_guide_bar
|
|
// Access: Public
|
|
// Description: 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;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PStatServer::remove_user_guide_bar
|
|
// Access: Public
|
|
// Description: 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();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PStatServer::find_user_guide_bar
|
|
// Access: Public
|
|
// Description: 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;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PStatServer::user_guide_bars_changed
|
|
// Access: Priate
|
|
// Description: 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();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PStatServer::is_thread_safe
|
|
// Access: Public
|
|
// Description: 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;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PStatServer::connection_reset
|
|
// Access: Protected, Virtual
|
|
// Description: 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);
|
|
}
|
|
}
|