Compare commits

..

9 Commits

Author SHA1 Message Date
Automatic Release Builder
17f86567f1 new version: 2020.1.3 2020-06-25 15:40:15 +01:00
James Turner
059bad1840 Fix zero-interval repeat on pick/knob animation
See:
https://sourceforge.net/p/flightgear/codetickets/2241/

Note did not adjust bug in knob-animation ignoring repeatable flag
(always marked repeatable) since this might break some aircraft.
2020-06-20 16:11:08 +01:00
James Turner
b7d4acc67d Fix compilation with Boost >= 1.73 2020-06-20 16:10:56 +01:00
Automatic Release Builder
af8b27032f new version: 2020.1.2 2020-05-18 11:54:22 +01:00
James Turner
f76b72e2f3 Adjust RelWithDebInfo optimisation for Clang
This should give a bit of speed boost to macOS official builds.
2020-05-14 13:50:58 +01:00
James Turner
59512aea1f Windows: ensure RelWithDebInfo is fast
Default CMake RelWithDebInfo pessimizes inlining
2020-05-10 19:46:27 +01:00
James Turner
96618a2e26 Enable old-style texture compression
Thanks to Dany for tracking this down!
2020-05-10 19:46:27 +01:00
James Turner
635f460dd5 Nasal: improve message for non-object member access
Based on some discussion on this ticket:

https://sourceforge.net/p/flightgear/codetickets/2186/

Make this message slightly clearer.
2020-05-03 12:22:23 +01:00
James Turner
36316e8859 Add Canvas::Image.dirtyPixels() 2020-05-03 12:22:11 +01:00
84 changed files with 1577 additions and 3227 deletions

View File

@@ -10,12 +10,6 @@ if(COMMAND cmake_policy)
if(POLICY CMP0067)
cmake_policy(SET CMP0067 NEW)
endif()
# OpenGL VND policy : use the old definition for now, until we can audit this
if(POLICY CMP0072)
cmake_policy(SET CMP0072 OLD)
endif()
if(POLICY CMP0093)
cmake_policy(SET CMP0093 NEW)
endif()
@@ -49,7 +43,7 @@ set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED YES)
# read 'version' file into a variable (stripping any newlines or spaces)
file(READ simgear-version versionFile)
file(READ version versionFile)
string(STRIP ${versionFile} SIMGEAR_VERSION)
project(SimGear VERSION ${SIMGEAR_VERSION} LANGUAGES C CXX)
@@ -423,8 +417,16 @@ if(CMAKE_COMPILER_IS_GNUCXX)
message(WARNING "GCC 4.4 will be required soon, please upgrade")
endif()
if (X86 OR X86_64)
set(SIMD_COMPILER_FLAGS "-msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize")
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_C_FLAGS
"${CMAKE_C_FLAGS} -O0 -fno-omit-frame-pointer -fno-inline")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -O0 -fno-omit-frame-pointer -fno-inline")
elseif (ENABLE_SIMD)
if (X86 OR X86_64)
set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize")
endif()
endif()
# certain GCC versions don't provide the atomic builds, and hence
@@ -433,10 +435,6 @@ if(CMAKE_COMPILER_IS_GNUCXX)
check_cxx_source_compiles(
"int main() { unsigned mValue; return __sync_add_and_fetch(&mValue, 1); }"
GCC_ATOMIC_BUILTINS_FOUND)
# override CMake default RelWithDebInfo flags.
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG")
endif(CMAKE_COMPILER_IS_GNUCXX)
if (CLANG)
@@ -451,7 +449,16 @@ if (CLANG)
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG")
set(SIMD_COMPILER_FLAGS "-msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize")
if (ENABLE_SIMD)
if (X86 OR X86_64)
set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize")
# propogate to the RelWithDebInfo flags
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} -g -DNDEBUG")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELEASE} -g -DNDEBUG")
endif()
endif()
endif()
if (ENABLE_OPENMP)
@@ -483,8 +490,14 @@ if(WIN32)
if(MSVC)
set(MSVC_FLAGS "-DWIN32 -DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS /MP")
if (X86)
set(SIMD_COMPILER_FLAGS "/arch:SSE /arch:SSE2")
if(ENABLE_SIMD)
if (X86)
SET(CMAKE_C_FLAGS_RELEASE "/O2 /arch:SSE /arch:SSE2")
SET(CMAKE_CXX_FLAGS_RELEASE "/O2 /arch:SSE /arch:SSE2")
else()
SET(CMAKE_C_FLAGS_RELEASE "/O2")
SET(CMAKE_CXX_FLAGS_RELEASE "/O2")
endif()
endif()
if (NOT OSG_FSTREAM_EXPORT_FIXED)
@@ -508,20 +521,6 @@ if(WIN32)
set( RT_LIBRARY "winmm" )
endif(WIN32)
# append the SIMD flags if requested
if (ENABLE_SIMD)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SIMD_COMPILER_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SIMD_COMPILER_FLAGS}")
# set for multi-configuration generators
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${SIMD_COMPILER_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${SIMD_COMPILER_FLAGS}")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} ${SIMD_COMPILER_FLAGS}")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${SIMD_COMPILER_FLAGS}")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS_C} ${MSVC_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS_CXX} ${MSVC_FLAGS} ${BOOST_CXX_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MSVC_LD_FLAGS}")

View File

@@ -1 +0,0 @@
2020.3.1

View File

@@ -167,8 +167,7 @@ target_link_libraries(SimGearCore PRIVATE
${CMAKE_THREAD_LIBS_INIT}
${COCOA_LIBRARY}
${CURL_LIBRARIES}
${WINSOCK_LIBRARY}
${SHLWAPI_LIBRARY})
${WINSOCK_LIBRARY})
if(SYSTEM_EXPAT)
target_link_libraries(SimGearCore PRIVATE ${EXPAT_LIBRARIES})

View File

@@ -850,7 +850,7 @@ namespace canvas
void fillRow(GLubyte* row, GLuint pixel, GLuint width, GLuint pixelBytes)
{
GLubyte* dst = row;
for (GLuint x = 0; x < width; ++x) {
for (int x = 0; x < width; ++x) {
memcpy(dst, &pixel, pixelBytes);
dst += pixelBytes;
}

View File

@@ -73,7 +73,7 @@ namespace canvas
int stretch,
uint8_t alignment )
{
ItemData item_data = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
ItemData item_data = {0};
item_data.layout_item = item;
item_data.stretch = std::max(0, stretch);

View File

@@ -26,7 +26,7 @@
#include <vector>
#include <memory> // for std::unique_ptr
#include <simgear/debug/LogCallback.hxx>
#include <simgear/debug/logstream.hxx>
namespace simgear
{

View File

@@ -1,10 +1,7 @@
include (SimGearComponent)
set(HEADERS debug_types.h
logstream.hxx BufferedLogCallback.hxx OsgIoCapture.hxx
LogCallback.hxx LogEntry.hxx)
set(SOURCES logstream.cxx BufferedLogCallback.cxx
LogCallback.cxx LogEntry.cxx)
set(HEADERS debug_types.h logstream.hxx BufferedLogCallback.hxx OsgIoCapture.hxx)
set(SOURCES logstream.cxx BufferedLogCallback.cxx)
simgear_component(debug debug "${SOURCES}" "${HEADERS}")

View File

@@ -1,116 +0,0 @@
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include "LogCallback.hxx"
using namespace simgear;
LogCallback::LogCallback(sgDebugClass c, sgDebugPriority p) : m_class(c),
m_priority(p)
{
}
void LogCallback::operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage)
{
// override me
}
bool LogCallback::doProcessEntry(const LogEntry& e)
{
return false;
}
void LogCallback::processEntry(const LogEntry& e)
{
if (doProcessEntry(e))
return; // derived class used the new API
// call the old API
(*this)(e.debugClass, e.debugPriority, e.file, e.line, e.message);
}
bool LogCallback::shouldLog(sgDebugClass c, sgDebugPriority p) const
{
if ((c & m_class) != 0 && p >= m_priority)
return true;
if (c == SG_OSG) // always have OSG logging as it OSG logging is configured separately.
return true;
return false;
}
void LogCallback::setLogLevels(sgDebugClass c, sgDebugPriority p)
{
m_priority = p;
m_class = c;
}
const char* LogCallback::debugPriorityToString(sgDebugPriority p)
{
switch (p) {
case SG_DEV_ALERT:
case SG_ALERT:
return "ALRT";
case SG_BULK: return "BULK";
case SG_DEBUG: return "DBUG";
case SG_MANDATORY_INFO:
case SG_INFO:
return "INFO";
case SG_POPUP: return "POPU";
case SG_DEV_WARN:
case SG_WARN:
return "WARN";
default: return "UNKN";
}
}
const char* LogCallback::debugClassToString(sgDebugClass c)
{
switch (c) {
case SG_NONE: return "none";
case SG_TERRAIN: return "terrain";
case SG_ASTRO: return "astro";
case SG_FLIGHT: return "flight";
case SG_INPUT: return "input";
case SG_GL: return "opengl";
case SG_VIEW: return "view";
case SG_COCKPIT: return "cockpit";
case SG_GENERAL: return "general";
case SG_MATH: return "math";
case SG_EVENT: return "event";
case SG_AIRCRAFT: return "aircraft";
case SG_AUTOPILOT: return "autopilot";
case SG_IO: return "io";
case SG_CLIPPER: return "clipper";
case SG_NETWORK: return "network";
case SG_ATC: return "atc";
case SG_NASAL: return "nasal";
case SG_INSTR: return "instruments";
case SG_SYSTEMS: return "systems";
case SG_AI: return "ai";
case SG_ENVIRONMENT: return "environment";
case SG_SOUND: return "sound";
case SG_NAVAID: return "navaid";
case SG_GUI: return "gui";
case SG_TERRASYNC: return "terrasync";
case SG_PARTICLES: return "particles";
case SG_HEADLESS: return "headless";
case SG_OSG: return "OSG";
default: return "unknown";
}
}

View File

@@ -1,56 +0,0 @@
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#pragma once
#include <string>
#include "LogEntry.hxx"
#include "debug_types.h"
namespace simgear {
class LogCallback
{
public:
virtual ~LogCallback() = default;
// newer API: return true if you handled the message, otherwise
// the old API will be called
virtual bool doProcessEntry(const LogEntry& e);
// old API, kept for compatability
virtual void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage);
void setLogLevels(sgDebugClass c, sgDebugPriority p);
void processEntry(const LogEntry& e);
protected:
LogCallback(sgDebugClass c, sgDebugPriority p);
bool shouldLog(sgDebugClass c, sgDebugPriority p) const;
static const char* debugClassToString(sgDebugClass c);
static const char* debugPriorityToString(sgDebugPriority p);
private:
sgDebugClass m_class;
sgDebugPriority m_priority;
};
} // namespace simgear

View File

@@ -1,45 +0,0 @@
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include "LogEntry.hxx"
#include <cstring> // for strdup
namespace simgear {
LogEntry::~LogEntry()
{
if (freeFilename) {
free(const_cast<char*>(file));
}
}
LogEntry::LogEntry(const LogEntry& c) : debugClass(c.debugClass),
debugPriority(c.debugPriority),
originalPriority(c.originalPriority),
file(c.file),
line(c.line),
message(c.message),
freeFilename(c.freeFilename)
{
if (c.freeFilename) {
file = strdup(c.file);
}
}
} // namespace simgear

View File

@@ -1,54 +0,0 @@
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#pragma once
#include <string>
#include "debug_types.h"
namespace simgear {
/**
* storage of a single log entry. This is used to pass log entries from
* the various threads to the logging thread, and also to store the startup
* entries
*/
class LogEntry
{
public:
LogEntry(sgDebugClass c, sgDebugPriority p,
sgDebugPriority op,
const char* f, int l, const std::string& msg) : debugClass(c), debugPriority(p), originalPriority(op),
file(f), line(l),
message(msg)
{
}
LogEntry(const LogEntry& c);
LogEntry& operator=(const LogEntry& c) = delete;
~LogEntry();
const sgDebugClass debugClass;
const sgDebugPriority debugPriority;
const sgDebugPriority originalPriority;
const char* file;
const int line;
const std::string message;
bool freeFilename = false; ///< if true, we own, and therefore need to free, the memory pointed to by 'file'
};
} // namespace simgear

View File

@@ -2,7 +2,8 @@
#include <osg/Notify>
#include <simgear/debug/logstream.hxx>
using namespace osg;
/**
* merge OSG output into our logging system, so it gets recorded to file,

View File

@@ -1,5 +1,3 @@
#pragma once
/** \file debug_types.h
* Define the various logging classes and priorities
*/
@@ -54,17 +52,16 @@ typedef enum {
* appended, or the priority Nasal reports to compiled code will change.
*/
typedef enum {
SG_BULK = 1, // For frequent messages
SG_DEBUG, // Less frequent debug type messages
SG_INFO, // Informatory messages
SG_WARN, // Possible impending problem
SG_ALERT, // Very possible impending problem
SG_POPUP, // Severe enough to alert using a pop-up window
SG_BULK = 1, // For frequent messages
SG_DEBUG, // Less frequent debug type messages
SG_INFO, // Informatory messages
SG_WARN, // Possible impending problem
SG_ALERT, // Very possible impending problem
SG_POPUP, // Severe enough to alert using a pop-up window
// SG_EXIT, // Problem (no core)
// SG_ABORT // Abandon ship (core)
SG_DEV_WARN, // Warning for developers, translated to other priority
SG_DEV_ALERT, // Alert for developers, translated
SG_MANDATORY_INFO // information, but should always be shown
SG_DEV_WARN, // Warning for developers, translated to other priority
SG_DEV_ALERT // Alert for developers, translated
} sgDebugPriority;

View File

@@ -35,7 +35,6 @@
#include <simgear/threads/SGThread.hxx>
#include <simgear/threads/SGQueue.hxx>
#include "LogCallback.hxx"
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/strutils.hxx>
@@ -48,9 +47,86 @@
#include <io.h>
#endif
//////////////////////////////////////////////////////////////////////////////
namespace simgear
{
LogCallback::LogCallback(sgDebugClass c, sgDebugPriority p) :
m_class(c),
m_priority(p)
{
}
bool LogCallback::shouldLog(sgDebugClass c, sgDebugPriority p) const
{
if ((c & m_class) != 0 && p >= m_priority)
return true;
if (c == SG_OSG) // always have OSG logging as it OSG logging is configured separately.
return true;
return false;
}
void LogCallback::setLogLevels( sgDebugClass c, sgDebugPriority p )
{
m_priority = p;
m_class = c;
}
const char* LogCallback::debugPriorityToString(sgDebugPriority p)
{
switch (p) {
case SG_ALERT: return "ALRT";
case SG_BULK: return "BULK";
case SG_DEBUG: return "DBUG";
case SG_INFO: return "INFO";
case SG_POPUP: return "POPU";
case SG_WARN: return "WARN";
default: return "UNKN";
}
}
const char* LogCallback::debugClassToString(sgDebugClass c)
{
switch (c) {
case SG_NONE: return "none";
case SG_TERRAIN: return "terrain";
case SG_ASTRO: return "astro";
case SG_FLIGHT: return "flight";
case SG_INPUT: return "input";
case SG_GL: return "opengl";
case SG_VIEW: return "view";
case SG_COCKPIT: return "cockpit";
case SG_GENERAL: return "general";
case SG_MATH: return "math";
case SG_EVENT: return "event";
case SG_AIRCRAFT: return "aircraft";
case SG_AUTOPILOT: return "autopilot";
case SG_IO: return "io";
case SG_CLIPPER: return "clipper";
case SG_NETWORK: return "network";
case SG_ATC: return "atc";
case SG_NASAL: return "nasal";
case SG_INSTR: return "instruments";
case SG_SYSTEMS: return "systems";
case SG_AI: return "ai";
case SG_ENVIRONMENT:return "environment";
case SG_SOUND: return "sound";
case SG_NAVAID: return "navaid";
case SG_GUI: return "gui";
case SG_TERRASYNC: return "terrasync";
case SG_PARTICLES: return "particles";
case SG_HEADLESS: return "headless";
case SG_OSG: return "OSG";
default: return "unknown";
}
}
} // of namespace simgear
//////////////////////////////////////////////////////////////////////////////
class FileLogCallback : public simgear::LogCallback
{
@@ -63,8 +139,8 @@ public:
logTimer.stamp();
}
void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& message) override
virtual void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& message)
{
if (!shouldLog(c, p)) return;
@@ -120,8 +196,8 @@ public:
}
#endif
void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage) override
virtual void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage)
{
if (!shouldLog(c, p)) return;
//fprintf(stderr, "%s\n", aMessage.c_str());
@@ -150,8 +226,8 @@ public:
{
}
void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage) override
virtual void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage)
{
if (!shouldLog(c, p)) return;
@@ -166,6 +242,28 @@ public:
class logstream::LogStreamPrivate : public SGThread
{
private:
/**
* storage of a single log entry. This is used to pass log entries from
* the various threads to the logging thread, and also to store the startup
* entries
*/
class LogEntry
{
public:
LogEntry(sgDebugClass c, sgDebugPriority p,
const char* f, int l, const std::string& msg) :
debugClass(c), debugPriority(p), file(f), line(l),
message(msg)
{
}
const sgDebugClass debugClass;
const sgDebugPriority debugPriority;
const char* file;
const int line;
const std::string message;
};
/**
* RAII object to pause the logging thread if it's running, and restart it.
* used to safely make configuration changes.
@@ -303,20 +401,13 @@ public:
~LogStreamPrivate()
{
removeCallbacks();
// house-keeping, avoid leak warnings if we exit before disabling
// startup logging
{
std::lock_guard<std::mutex> g(m_lock);
clearStartupEntriesLocked();
}
}
std::mutex m_lock;
SGBlockingQueue<simgear::LogEntry> m_entries;
SGBlockingQueue<LogEntry> m_entries;
// log entries posted during startup
std::vector<simgear::LogEntry> m_startupEntries;
std::vector<LogEntry> m_startupEntries;
bool m_startupLogging = false;
typedef std::vector<simgear::LogCallback*> CallbackVec;
@@ -339,8 +430,6 @@ public:
// test suite mode.
bool m_testMode = false;
std::vector<std::string> _popupMessages;
void startLog()
{
std::lock_guard<std::mutex> g(m_lock);
@@ -358,19 +447,14 @@ public:
{
std::lock_guard<std::mutex> g(m_lock);
m_startupLogging = on;
clearStartupEntriesLocked();
m_startupEntries.clear();
}
}
void clearStartupEntriesLocked()
{
m_startupEntries.clear();
}
void run() override
virtual void run()
{
while (1) {
simgear::LogEntry entry(m_entries.pop());
LogEntry entry(m_entries.pop());
// special marker entry detected, terminate the thread since we are
// making a configuration change or quitting the app
if ((entry.debugClass == SG_NONE) && entry.file && !strcmp(entry.file, "done")) {
@@ -386,7 +470,8 @@ public:
}
// submit to each installed callback in turn
for (simgear::LogCallback* cb : m_callbacks) {
cb->processEntry(entry);
(*cb)(entry.debugClass, entry.debugPriority,
entry.file, entry.line, entry.message);
}
} // of main thread loop
}
@@ -401,7 +486,7 @@ public:
// log a special marker value, which will cause the thread to wakeup,
// and then exit
log(SG_NONE, SG_ALERT, "done", -1, "", false);
log(SG_NONE, SG_ALERT, "done", -1, "");
}
join();
@@ -416,8 +501,9 @@ public:
// we clear startup entries not using this, so always safe to run
// this code, container will simply be empty
for (const auto& entry : m_startupEntries) {
cb->processEntry(entry);
for (auto entry : m_startupEntries) {
(*cb)(entry.debugClass, entry.debugPriority,
entry.file, entry.line, entry.message);
}
}
@@ -445,9 +531,9 @@ public:
PauseThread pause(this);
m_logPriority = p;
m_logClass = c;
for (auto cb : m_consoleCallbacks) {
cb->setLogLevels(c, p);
}
for (auto cb : m_consoleCallbacks) {
cb->setLogLevels(c, p);
}
}
bool would_log( sgDebugClass c, sgDebugPriority p ) const
@@ -465,17 +551,14 @@ public:
}
void log( sgDebugClass c, sgDebugPriority p,
const char* fileName, int line, const std::string& msg,
bool freeFilename)
const char* fileName, int line, const std::string& msg)
{
auto tp = translatePriority(p);
p = translatePriority(p);
if (!m_fileLine) {
/* This prevents output of file:line in StderrLogCallback. */
line = -line;
}
simgear::LogEntry entry(c, tp, p, fileName, line, msg);
entry.freeFilename = freeFilename;
LogEntry entry(c, p, fileName, line, msg);
m_entries.push(entry);
}
@@ -506,8 +589,8 @@ logstream::logstream()
logstream::~logstream()
{
popup_msgs.clear();
d->stop();
d.reset();
}
void
@@ -542,14 +625,7 @@ void
logstream::log( sgDebugClass c, sgDebugPriority p,
const char* fileName, int line, const std::string& msg)
{
d->log(c, p, fileName, line, msg, false);
}
void
logstream::logCopyingFilename( sgDebugClass c, sgDebugPriority p,
const char* fileName, int line, const std::string& msg)
{
d->log(c, p, strdup(fileName), line, msg, true);
d->log(c, p, fileName, line, msg);
}
@@ -610,18 +686,17 @@ void logstream::hexdump(sgDebugClass c, sgDebugPriority p, const char* fileName,
void
logstream::popup( const std::string& msg)
{
std::lock_guard<std::mutex> g(d->m_lock);
d->_popupMessages.push_back(msg);
popup_msgs.push_back(msg);
}
std::string
logstream::get_popup()
{
std::string rv;
std::lock_guard<std::mutex> g(d->m_lock);
if (!d->_popupMessages.empty()) {
rv = d->_popupMessages.front();
d->_popupMessages.erase(d->_popupMessages.begin());
std::string rv = "";
if (!popup_msgs.empty())
{
rv = popup_msgs.front();
popup_msgs.erase(popup_msgs.begin());
}
return rv;
}
@@ -629,8 +704,7 @@ logstream::get_popup()
bool
logstream::has_popup()
{
std::lock_guard<std::mutex> g(d->m_lock);
return !d->_popupMessages.empty();
return (popup_msgs.size() > 0) ? true : false;
}
bool
@@ -663,16 +737,6 @@ logstream::set_log_classes( sgDebugClass c)
d->setLogLevels(c, d->m_logPriority);
}
sgDebugPriority logstream::priorityFromString(const std::string& s)
{
if (s == "bulk") return SG_BULK;
if (s == "debug") return SG_DEBUG;
if (s == "info") return SG_INFO;
if (s == "warn") return SG_WARN;
if (s == "alert") return SG_ALERT;
throw std::invalid_argument("Couldn't parse log prioirty:" + s);
}
logstream&
sglog()

View File

@@ -37,8 +37,27 @@ class SGPath;
namespace simgear
{
class LogCallback
{
public:
virtual ~LogCallback() {}
virtual void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage) = 0;
void setLogLevels(sgDebugClass c, sgDebugPriority p);
protected:
LogCallback(sgDebugClass c, sgDebugPriority p);
bool shouldLog(sgDebugClass c, sgDebugPriority p) const;
static const char* debugClassToString(sgDebugClass c);
static const char* debugPriorityToString(sgDebugPriority p);
private:
sgDebugClass m_class;
sgDebugPriority m_priority;
};
class LogCallback;
/**
* Helper force a console on platforms where it might optional, when
* we need to show a console. This basically means Windows at the
@@ -86,11 +105,6 @@ public:
sgDebugPriority get_log_priority() const;
/**
@brief convert a string value to a log prioirty.
throws std::invalid_argument if the string is not valid
*/
static sgDebugPriority priorityFromString(const std::string& s);
/**
* set developer mode on/off. In developer mode, SG_DEV_WARN messags
* are treated as warnings. In normal (non-developer) mode they are
@@ -110,14 +124,6 @@ public:
void log( sgDebugClass c, sgDebugPriority p,
const char* fileName, int line, const std::string& msg);
// overload of above, which can transfer ownership of the file-name.
// this is unecesary overhead when logging from C++, since __FILE__ points
// to constant data, but it's needed when the filename is Nasal data (for
// example) since during shutdown the filename is freed by Nasal GC
// asynchronously with the logging thread.
void logCopyingFilename( sgDebugClass c, sgDebugPriority p,
const char* fileName, int line, const std::string& msg);
/**
* output formatted hex dump of memory block
*/
@@ -179,6 +185,8 @@ private:
// constructor
logstream();
std::vector<std::string> popup_msgs;
class LogStreamPrivate;
std::unique_ptr<LogStreamPrivate> d;

View File

@@ -631,15 +631,11 @@ bool SGMetar::scanWind()
int dir;
if (!strncmp(m, "VRB", 3))
m += 3, dir = -1;
else if (!strncmp(m, "///", 3)) // direction not measurable
m += 3, dir = -1;
else if (!scanNumber(&m, &dir, 3))
return false;
int i;
if (!strncmp(m, "//", 2)) // speed not measurable
m += 2, i = -1;
else if (!scanNumber(&m, &i, 2, 3))
if (!scanNumber(&m, &i, 2, 3))
return false;
double speed = i;
@@ -659,8 +655,6 @@ bool SGMetar::scanWind()
m += 3, factor = SG_KMH_TO_MPS;
else if (!strncmp(m, "MPS", 3))
m += 3, factor = 1.0;
else if (!strncmp(m, " ", 1)) // default to Knots
factor = SG_KT_TO_MPS;
else
return false;
if (!scanBoundary(&m))
@@ -958,8 +952,6 @@ bool SGMetar::scanWeather()
weather += string(a->text) + " ";
if (!strcmp(a->id, "RA"))
_rain = w.intensity;
else if (!strcmp(a->id, "DZ"))
_rain = LIGHT;
else if (!strcmp(a->id, "HA"))
_hail = w.intensity;
else if (!strcmp(a->id, "SN"))

View File

@@ -76,29 +76,12 @@ void test_sensor_failure_cloud()
SG_CHECK_EQUAL_EP2(m1.getPressure_hPa(), 1025, TEST_EPSILON);
}
void test_sensor_failure_wind()
{
SGMetar m1("2020/10/23 16:55 LIVD 231655Z /////KT 9999 OVC025 10/08 Q1020 RMK OVC VIS MIN 9999 BLU");
SG_CHECK_EQUAL(m1.getWindDir(), -1);
SG_CHECK_EQUAL_EP2(m1.getWindSpeed_kt(), -1, TEST_EPSILON);
}
void test_wind_unit_not_specified()
{
SGMetar m1("2020/10/23 11:58 KLSV 231158Z 05010G14 10SM CLR 16/M04 A2992 RMK SLPNO WND DATA ESTMD ALSTG/SLP ESTMD 10320 20124 5//// $");
SG_CHECK_EQUAL(m1.getWindDir(), 50);
SG_CHECK_EQUAL_EP2(m1.getWindSpeed_kt(), 10.0, TEST_EPSILON);
SG_CHECK_EQUAL_EP2(m1.getGustSpeed_kt(), 14.0, TEST_EPSILON);
}
int main(int argc, char* argv[])
{
try {
test_basic();
test_sensor_failure_weather();
test_sensor_failure_cloud();
test_sensor_failure_wind();
test_wind_unit_not_specified();
} catch (sg_exception& e) {
cerr << "got exception:" << e.getMessage() << endl;
return -1;

View File

@@ -35,12 +35,10 @@ set(SOURCES
sg_socket.cxx
sg_socket_udp.cxx
HTTPClient.cxx
HTTPTestApi_private.hxx
HTTPFileRequest.cxx
HTTPMemoryRequest.cxx
HTTPRequest.cxx
HTTPRepository.cxx
HTTPRepository_private.hxx
untar.cxx
)
@@ -83,7 +81,6 @@ add_test(binobj ${EXECUTABLE_OUTPUT_PATH}/test_binobj)
add_executable(test_repository test_repository.cxx)
target_link_libraries(test_repository ${TEST_LIBS})
target_compile_definitions(test_repository PUBLIC BUILDING_TESTSUITE)
add_test(http_repository ${EXECUTABLE_OUTPUT_PATH}/test_repository)
add_executable(test_untar test_untar.cxx)

View File

@@ -156,13 +156,8 @@ static void dnscbTXT(struct dns_ctx *ctx, struct dns_rr_txt *result, void *data)
r->ttl = result->dnstxt_ttl;
for (int i = 0; i < result->dnstxt_nrr; i++) {
//TODO: interprete the .len field of dnstxt_txt?
auto rawTxt = reinterpret_cast<char*>(result->dnstxt_txt[i].txt);
if (!rawTxt) {
continue;
}
const string txt{rawTxt};
r->entries.push_back(txt);
string txt = string((char*)result->dnstxt_txt[i].txt);
r->entries.push_back( txt );
string_list tokens = simgear::strutils::split( txt, "=", 1 );
if( tokens.size() == 2 ) {
r->attributes[tokens[0]] = tokens[1];

View File

@@ -37,6 +37,7 @@
#include <simgear/simgear_config.h>
#include <curl/multi.h>
#include <simgear/io/sg_netChat.hxx>
@@ -46,9 +47,6 @@
#include <simgear/timing/timestamp.hxx>
#include <simgear/structure/exception.hxx>
#include "HTTPClient_private.hxx"
#include "HTTPTestApi_private.hxx"
#if defined( HAVE_VERSION_H ) && HAVE_VERSION_H
#include "version.h"
#else
@@ -66,20 +64,48 @@ namespace HTTP
extern const int DEFAULT_HTTP_PORT = 80;
const char* CONTENT_TYPE_URL_ENCODED = "application/x-www-form-urlencoded";
void Client::ClientPrivate::createCurlMulti() {
curlMulti = curl_multi_init();
// see https://curl.haxx.se/libcurl/c/CURLMOPT_PIPELINING.html
// we request HTTP 1.1 pipelining
curl_multi_setopt(curlMulti, CURLMOPT_PIPELINING, 1 /* aka CURLPIPE_HTTP1 */);
class Connection;
typedef std::multimap<std::string, Connection*> ConnectionDict;
typedef std::list<Request_ptr> RequestList;
class Client::ClientPrivate
{
public:
CURLM* curlMulti;
void createCurlMulti()
{
curlMulti = curl_multi_init();
// see https://curl.haxx.se/libcurl/c/CURLMOPT_PIPELINING.html
// we request HTTP 1.1 pipelining
curl_multi_setopt(curlMulti, CURLMOPT_PIPELINING, 1 /* aka CURLPIPE_HTTP1 */);
#if (LIBCURL_VERSION_MINOR >= 30)
curl_multi_setopt(curlMulti, CURLMOPT_MAX_TOTAL_CONNECTIONS,
(long)maxConnections);
curl_multi_setopt(curlMulti, CURLMOPT_MAX_PIPELINE_LENGTH,
(long)maxPipelineDepth);
curl_multi_setopt(curlMulti, CURLMOPT_MAX_HOST_CONNECTIONS,
(long)maxHostConnections);
curl_multi_setopt(curlMulti, CURLMOPT_MAX_TOTAL_CONNECTIONS, (long) maxConnections);
curl_multi_setopt(curlMulti, CURLMOPT_MAX_PIPELINE_LENGTH,
(long) maxPipelineDepth);
curl_multi_setopt(curlMulti, CURLMOPT_MAX_HOST_CONNECTIONS,
(long) maxHostConnections);
#endif
}
}
typedef std::map<Request_ptr, CURL*> RequestCurlMap;
RequestCurlMap requests;
std::string userAgent;
std::string proxy;
int proxyPort;
std::string proxyAuth;
unsigned int maxConnections;
unsigned int maxHostConnections;
unsigned int maxPipelineDepth;
RequestList pendingRequests;
SGTimeStamp timeTransferSample;
unsigned int bytesTransferred;
unsigned int lastTransferRate;
uint64_t totalBytesDownloaded;
};
Client::Client() :
d(new ClientPrivate)
@@ -94,8 +120,6 @@ Client::Client() :
d->maxPipelineDepth = 5;
setUserAgent("SimGear-" SG_STRINGIZE(SIMGEAR_VERSION));
d->tlsCertificatePath = SGPath::fromEnv("SIMGEAR_TLS_CERT_PATH");
static bool didInitCurlGlobal = false;
static std::mutex initMutex;
@@ -137,14 +161,6 @@ void Client::setMaxPipelineDepth(unsigned int depth)
#endif
}
void Client::reset()
{
curl_multi_cleanup(d->curlMulti);
d.reset(new ClientPrivate);
d->tlsCertificatePath = SGPath::fromEnv("SIMGEAR_TLS_CERT_PATH");
d->createCurlMulti();
}
void Client::update(int waitTimeout)
{
if (d->requests.empty()) {
@@ -155,22 +171,32 @@ void Client::update(int waitTimeout)
}
int remainingActive, messagesInQueue;
int numFds;
CURLMcode mc = curl_multi_wait(d->curlMulti, NULL, 0, waitTimeout, &numFds);
if (mc != CURLM_OK) {
SG_LOG(SG_IO, SG_WARN, "curl_multi_wait failed:" << curl_multi_strerror(mc));
return;
#if defined(SG_MAC)
// Mac 10.8 libCurl lacks this, let's keep compat for now
fd_set curlReadFDs, curlWriteFDs, curlErrorFDs;
int maxFD;
curl_multi_fdset(d->curlMulti,
&curlReadFDs,
&curlWriteFDs,
&curlErrorFDs,
&maxFD);
struct timeval timeout;
long t;
curl_multi_timeout(d->curlMulti, &t);
if ((t < 0) || (t > waitTimeout)) {
t = waitTimeout;
}
mc = curl_multi_perform(d->curlMulti, &remainingActive);
if (mc == CURLM_CALL_MULTI_PERFORM) {
// we could loop here, but don't want to get blocked
// also this shouldn't ocurr in any modern libCurl
curl_multi_perform(d->curlMulti, &remainingActive);
} else if (mc != CURLM_OK) {
SG_LOG(SG_IO, SG_WARN, "curl_multi_perform failed:" << curl_multi_strerror(mc));
return;
}
timeout.tv_sec = t / 1000;
timeout.tv_usec = (t % 1000) * 1000;
::select(maxFD, &curlReadFDs, &curlWriteFDs, &curlErrorFDs, &timeout);
#else
int numFds;
curl_multi_wait(d->curlMulti, NULL, 0, waitTimeout, &numFds);
#endif
curl_multi_perform(d->curlMulti, &remainingActive);
CURLMsg* msg;
while ((msg = curl_multi_info_read(d->curlMulti, &messagesInQueue))) {
@@ -195,23 +221,12 @@ void Client::update(int waitTimeout)
assert(it->second == e);
d->requests.erase(it);
bool doProcess = true;
if (d->testsuiteResponseDoneCallback) {
doProcess =
!d->testsuiteResponseDoneCallback(msg->data.result, req);
}
if (doProcess) {
if (msg->data.result == 0) {
req->responseComplete();
} else {
SG_LOG(SG_IO, SG_WARN,
"CURL Result:" << msg->data.result << " "
<< curl_easy_strerror(msg->data.result));
req->setFailure(msg->data.result,
curl_easy_strerror(msg->data.result));
}
}
if (msg->data.result == 0) {
req->responseComplete();
} else {
SG_LOG(SG_IO, SG_WARN, "CURL Result:" << msg->data.result << " " << curl_easy_strerror(msg->data.result));
req->setFailure(msg->data.result, curl_easy_strerror(msg->data.result));
}
curl_multi_remove_handle(d->curlMulti, e);
curl_easy_cleanup(e);
@@ -270,11 +285,6 @@ void Client::makeRequest(const Request_ptr& r)
curl_easy_setopt(curlRequest, CURLOPT_FOLLOWLOCATION, 1);
if (!d->tlsCertificatePath.isNull()) {
const auto utf8 = d->tlsCertificatePath.utf8Str();
curl_easy_setopt(curlRequest, CURLOPT_CAINFO, utf8.c_str());
}
if (!d->proxy.empty()) {
curl_easy_setopt(curlRequest, CURLOPT_PROXY, d->proxy.c_str());
curl_easy_setopt(curlRequest, CURLOPT_PROXYPORT, d->proxyPort);
@@ -542,17 +552,6 @@ void Client::clearAllConnections()
d->createCurlMulti();
}
/////////////////////////////////////////////////////////////////////
void TestApi::setResponseDoneCallback(Client *cl, ResponseDoneCallback cb) {
cl->d->testsuiteResponseDoneCallback = cb;
}
void TestApi::markRequestAsFailed(Request_ptr req, int curlCode,
const std::string &message) {
req->setFailure(curlCode, message);
}
} // of namespace HTTP
} // of namespace simgear

View File

@@ -24,8 +24,7 @@
#ifndef SG_HTTP_CLIENT_HXX
#define SG_HTTP_CLIENT_HXX
#include <functional>
#include <memory> // for std::unique_ptr
#include <memory> // for std::unique_ptr
#include <stdint.h> // for uint_64t
#include <simgear/io/HTTPFileRequest.hxx>
@@ -48,8 +47,6 @@ public:
void update(int waitTimeout = 0);
void reset();
void makeRequest(const Request_ptr& r);
void cancelRequest(const Request_ptr& r, std::string reason = std::string());
@@ -126,7 +123,6 @@ private:
friend class Connection;
friend class Request;
friend class TestApi;
class ClientPrivate;
std::unique_ptr<ClientPrivate> d;

View File

@@ -1,68 +0,0 @@
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#pragma once
#include <list>
#include <map>
#include "HTTPClient.hxx"
#include "HTTPRequest.hxx"
#include <simgear/timing/timestamp.hxx>
#include <curl/multi.h>
namespace simgear {
namespace HTTP {
typedef std::list<Request_ptr> RequestList;
using ResponseDoneCallback =
std::function<bool(int curlResult, Request_ptr req)>;
class Client::ClientPrivate {
public:
CURLM *curlMulti;
void createCurlMulti();
typedef std::map<Request_ptr, CURL *> RequestCurlMap;
RequestCurlMap requests;
std::string userAgent;
std::string proxy;
int proxyPort;
std::string proxyAuth;
unsigned int maxConnections;
unsigned int maxHostConnections;
unsigned int maxPipelineDepth;
RequestList pendingRequests;
SGTimeStamp timeTransferSample;
unsigned int bytesTransferred;
unsigned int lastTransferRate;
uint64_t totalBytesDownloaded;
SGPath tlsCertificatePath;
// only used by unit-tests / test-api, but
// only costs us a pointe here to declare it.
ResponseDoneCallback testsuiteResponseDoneCallback;
};
} // namespace HTTP
} // namespace simgear

File diff suppressed because it is too large Load Diff

View File

@@ -20,7 +20,6 @@
#ifndef SG_IO_HTTP_REPOSITORY_HXX
#define SG_IO_HTTP_REPOSITORY_HXX
#include <functional>
#include <memory>
#include <simgear/misc/sg_path.hxx>
@@ -33,78 +32,49 @@ class HTTPRepoPrivate;
class HTTPRepository
{
public:
enum ResultCode {
REPO_NO_ERROR = 0,
REPO_ERROR_NOT_FOUND,
REPO_ERROR_SOCKET,
SVN_ERROR_XML,
SVN_ERROR_TXDELTA,
REPO_ERROR_IO,
REPO_ERROR_CHECKSUM,
REPO_ERROR_FILE_NOT_FOUND,
REPO_ERROR_HTTP,
REPO_ERROR_CANCELLED,
REPO_PARTIAL_UPDATE ///< repository is working, but file-level failures
///< occurred
};
HTTPRepository(const SGPath &root, HTTP::Client *cl);
virtual ~HTTPRepository();
virtual SGPath fsBase() const;
virtual void setBaseUrl(const std::string &url);
virtual std::string baseUrl() const;
virtual HTTP::Client *http() const;
virtual void update();
virtual bool isDoingSync() const;
virtual ResultCode failure() const;
virtual size_t bytesToDownload() const;
virtual size_t bytesDownloaded() const;
/**
* optionally provide the location of an installer copy of this
* repository. When a file is missing it will be copied from this tree.
*/
void setInstalledCopyPath(const SGPath &copyPath);
static std::string resultCodeAsString(ResultCode code);
enum class SyncAction { Add, Update, Delete, UpToDate };
enum EntryType { FileType, DirectoryType, TarballType };
struct SyncItem {
const std::string directory; // relative path in the repository
const EntryType type;
const std::string filename;
const SyncAction action;
const SGPath pathOnDisk; // path the entry does / will have
};
using SyncPredicate = std::function<bool(const SyncItem &item)>;
void setFilter(SyncPredicate sp);
struct Failure {
SGPath path;
ResultCode error;
enum ResultCode {
REPO_NO_ERROR = 0,
REPO_ERROR_NOT_FOUND,
REPO_ERROR_SOCKET,
SVN_ERROR_XML,
SVN_ERROR_TXDELTA,
REPO_ERROR_IO,
REPO_ERROR_CHECKSUM,
REPO_ERROR_FILE_NOT_FOUND,
REPO_ERROR_HTTP,
REPO_ERROR_CANCELLED,
REPO_PARTIAL_UPDATE
};
using FailureVec = std::vector<Failure>;
HTTPRepository(const SGPath& root, HTTP::Client* cl);
virtual ~HTTPRepository();
virtual SGPath fsBase() const;
virtual void setBaseUrl(const std::string& url);
virtual std::string baseUrl() const;
virtual HTTP::Client* http() const;
virtual void update();
virtual bool isDoingSync() const;
virtual ResultCode failure() const;
virtual size_t bytesToDownload() const;
virtual size_t bytesDownloaded() const;
/**
* @brief return file-level failures
* optionally provide the location of an installer copy of this
* repository. When a file is missing it will be copied from this tree.
*/
FailureVec failures() const;
void setInstalledCopyPath(const SGPath& copyPath);
static std::string resultCodeAsString(ResultCode code);
private:
private:
bool isBare() const;
std::unique_ptr<HTTPRepoPrivate> _d;

View File

@@ -1,122 +0,0 @@
// HTTPRepository.cxx -- plain HTTP TerraSync remote client
//
// Copyright (C) 20126 James Turner <zakalawe@mac.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program 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
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
// USA.
#pragma once
#include <memory>
#include <string>
#include <simgear/io/HTTPClient.hxx>
#include <simgear/misc/sg_path.hxx>
#include "HTTPRepository.hxx"
namespace simgear {
class HTTPDirectory;
using HTTPDirectory_ptr = std::unique_ptr<HTTPDirectory>;
class HTTPRepoGetRequest : public HTTP::Request {
public:
HTTPRepoGetRequest(HTTPDirectory *d, const std::string &u)
: HTTP::Request(u), _directory(d) {}
virtual void cancel();
size_t contentSize() const { return _contentSize; }
void setContentSize(size_t sz) { _contentSize = sz; }
protected:
HTTPDirectory *_directory;
size_t _contentSize = 0;
};
typedef SGSharedPtr<HTTPRepoGetRequest> RepoRequestPtr;
class HTTPRepoPrivate {
public:
struct HashCacheEntry {
std::string filePath;
time_t modTime;
size_t lengthBytes;
std::string hashHex;
};
typedef std::vector<HashCacheEntry> HashCache;
HashCache hashes;
int hashCacheDirty = 0;
HTTPRepository::FailureVec failures;
int maxPermittedFailures = 16;
HTTPRepoPrivate(HTTPRepository *parent)
: p(parent), isUpdating(false), status(HTTPRepository::REPO_NO_ERROR),
totalDownloaded(0) {
;
}
~HTTPRepoPrivate();
HTTPRepository *p; // link back to outer
HTTP::Client *http;
std::string baseUrl;
SGPath basePath;
bool isUpdating;
HTTPRepository::ResultCode status;
HTTPDirectory_ptr rootDir;
size_t totalDownloaded;
HTTPRepository::SyncPredicate syncPredicate;
HTTP::Request_ptr updateFile(HTTPDirectory *dir, const std::string &name,
size_t sz);
HTTP::Request_ptr updateDir(HTTPDirectory *dir, const std::string &hash,
size_t sz);
std::string hashForPath(const SGPath &p);
void updatedFileContents(const SGPath &p, const std::string &newHash);
void parseHashCache();
std::string computeHashForPath(const SGPath &p);
void writeHashCache();
void failedToGetRootIndex(HTTPRepository::ResultCode st);
void failedToUpdateChild(const SGPath &relativePath,
HTTPRepository::ResultCode fileStatus);
void updatedChildSuccessfully(const SGPath &relativePath);
typedef std::vector<RepoRequestPtr> RequestVector;
RequestVector queuedRequests, activeRequests;
void makeRequest(RepoRequestPtr req);
enum class RequestFinish { Done, Retry };
void finishedRequest(const RepoRequestPtr &req, RequestFinish retryRequest);
HTTPDirectory *getOrCreateDirectory(const std::string &path);
bool deleteDirectory(const std::string &relPath, const SGPath &absPath);
typedef std::vector<HTTPDirectory_ptr> DirectoryVector;
DirectoryVector directories;
SGPath installedCopyPath;
};
} // namespace simgear

View File

@@ -56,15 +56,6 @@ Request::~Request()
}
void Request::prepareForRetry() {
setReadyState(UNSENT);
_willClose = false;
_connectionCloseHeader = false;
_responseStatus = 0;
_responseLength = 0;
_receivedBodyBytes = 0;
}
//------------------------------------------------------------------------------
Request* Request::done(const Callback& cb)
{
@@ -202,16 +193,13 @@ void Request::onDone()
//------------------------------------------------------------------------------
void Request::onFail()
{
// log if we FAIELD< but not if we CANCELLED
if (_ready_state == FAILED) {
SG_LOG
(
SG_IO,
SG_INFO,
"request failed:" << url() << " : "
<< responseCode() << "/" << responseReason()
);
}
SG_LOG
(
SG_IO,
SG_INFO,
"request failed:" << url() << " : "
<< responseCode() << "/" << responseReason()
);
}
//------------------------------------------------------------------------------
@@ -356,17 +344,12 @@ void Request::setSuccess(int code)
//------------------------------------------------------------------------------
void Request::setFailure(int code, const std::string& reason)
{
// we use -1 for cancellation, don't be noisy in that case
if (code >= 0) {
SG_LOG(SG_IO, SG_WARN, "HTTP request: set failure:" << code << " reason " << reason);
}
_responseStatus = code;
_responseReason = reason;
if( !isComplete() ) {
setReadyState(code < 0 ? CANCELLED : FAILED);
}
if( !isComplete() )
setReadyState(FAILED);
}
//------------------------------------------------------------------------------
@@ -390,12 +373,6 @@ void Request::setReadyState(ReadyState state)
_cb_fail(this);
}
else if (state == CANCELLED )
{
onFail(); // do this for compatability
onAlways();
_cb_fail(this);
}
else
return;
@@ -425,7 +402,7 @@ bool Request::serverSupportsPipelining() const
//------------------------------------------------------------------------------
bool Request::isComplete() const
{
return _ready_state == DONE || _ready_state == FAILED || _ready_state == CANCELLED;
return _ready_state == DONE || _ready_state == FAILED;
}
//------------------------------------------------------------------------------

View File

@@ -54,8 +54,7 @@ public:
HEADERS_RECEIVED,
LOADING,
DONE,
FAILED,
CANCELLED
FAILED
};
virtual ~Request();
@@ -208,9 +207,7 @@ public:
*/
bool serverSupportsPipelining() const;
virtual void prepareForRetry();
protected:
protected:
Request(const std::string& url, const std::string method = "GET");
virtual void requestStart();
@@ -224,14 +221,12 @@ public:
virtual void onFail();
virtual void onAlways();
void setFailure(int code, const std::string& reason);
void setSuccess(int code);
void setFailure(int code, const std::string &reason);
private:
private:
friend class Client;
friend class Connection;
friend class ContentDecoder;
friend class TestApi;
Request(const Request&); // = delete;
Request& operator=(const Request&); // = delete;

View File

@@ -1,45 +0,0 @@
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#pragma once
#include <functional>
#include "HTTPRequest.hxx"
namespace simgear {
namespace HTTP {
class Client;
using ResponseDoneCallback =
std::function<bool(int curlResult, Request_ptr req)>;
/**
* @brief this API is for unit-testing HTTP code.
* Don't use it for anything else. It's for unit-testing.
*/
class TestApi {
public:
// alow test suite to manipulate requests to simulate network errors;
// without this, it's hard to provoke certain failures in a loop-back
// network sitation.
static void setResponseDoneCallback(Client *cl, ResponseDoneCallback cb);
static void markRequestAsFailed(Request_ptr req, int curlCode,
const std::string &message);
};
} // namespace HTTP
} // namespace simgear

View File

@@ -71,31 +71,21 @@ SGFile::~SGFile() {
std::string SGFile::computeHash()
{
if (!file_name.exists())
return {};
return std::string();
simgear::sha1nfo info;
sha1_init(&info);
// unique_ptr with custom deleter for exception safety
const int bufSize = 1024 * 1024;
std::unique_ptr<char, std::function<void(char*)>> buf{static_cast<char*>(malloc(bufSize)),
[](char* p) { free(p); }};
if (!buf) {
SG_LOG(SG_IO, SG_ALERT, "Failed to malloc buffer for SHA1 check");
}
char* buf = static_cast<char*>(malloc(1024 * 1024));
size_t readLen;
SGBinaryFile f(file_name);
if (!f.open(SG_IO_IN)) {
SG_LOG(SG_IO, SG_ALERT, "SGFile::computeHash: Failed to open " << file_name);
return {};
throw sg_io_exception("Couldn't open file for compute hash", file_name);
}
while ((readLen = f.read(buf.get(), bufSize)) > 0) {
sha1_write(&info, buf.get(), readLen);
while ((readLen = f.read(buf, 1024 * 1024)) > 0) {
sha1_write(&info, buf, readLen);
}
f.close();
free(buf);
std::string hashBytes((char*)sha1_result(&info), HASH_LENGTH);
return simgear::strutils::encodeHex(hashBytes);
}

View File

@@ -1,18 +1,16 @@
#include <cassert>
#include <cstdlib>
#include <errno.h>
#include <fcntl.h>
#include <functional>
#include <iostream>
#include <map>
#include <sstream>
#include <errno.h>
#include <fcntl.h>
#include <simgear/simgear_config.h>
#include "HTTPClient.hxx"
#include "HTTPRepository.hxx"
#include "HTTPTestApi_private.hxx"
#include "test_HTTP.hxx"
#include "HTTPRepository.hxx"
#include "HTTPClient.hxx"
#include <simgear/misc/strutils.hxx>
#include <simgear/misc/sg_hash.hxx>
@@ -27,8 +25,6 @@
using namespace simgear;
using TestApi = simgear::HTTP::TestApi;
std::string dataForFile(const std::string& parentName, const std::string& name, int revision)
{
std::ostringstream os;
@@ -49,9 +45,6 @@ std::string hashForData(const std::string& d)
return strutils::encodeHex(sha1_result(&info), HASH_LENGTH);
}
class TestRepoEntry;
using AccessCallback = std::function<void(TestRepoEntry &entry)>;
class TestRepoEntry
{
public:
@@ -77,8 +70,7 @@ public:
int requestCount;
bool getWillFail;
bool returnCorruptData;
AccessCallback accessCallback;
std::unique_ptr<SGCallback> accessCallback;
void clearRequestCounts();
@@ -278,8 +270,8 @@ public:
return;
}
if (entry->accessCallback) {
entry->accessCallback(*entry);
if (entry->accessCallback.get()) {
(*entry->accessCallback)();
}
if (entry->getWillFail) {
@@ -290,29 +282,20 @@ public:
entry->requestCount++;
std::string content;
bool closeSocket = false;
size_t contentSize = 0;
if (entry->returnCorruptData) {
content = dataForFile("!$£$!" + entry->parent->name,
"corrupt_" + entry->name,
entry->revision);
contentSize = content.size();
} else {
content = entry->data();
contentSize = content.size();
content = entry->data();
}
std::stringstream d;
d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
d << "Content-Length:" << contentSize << "\r\n";
d << "Content-Length:" << content.size() << "\r\n";
d << "\r\n"; // final CRLF to terminate the headers
d << content;
push(d.str().c_str());
if (closeSocket) {
closeWhenDone();
}
} else {
sendErrorResponse(404, false, "");
}
@@ -418,15 +401,6 @@ void waitForUpdateComplete(HTTP::Client* cl, HTTPRepository* repo)
std::cerr << "timed out" << std::endl;
}
void runForTime(HTTP::Client *cl, HTTPRepository *repo, int msec = 15) {
SGTimeStamp start(SGTimeStamp::now());
while (start.elapsedMSec() < msec) {
cl->update();
testServer.poll();
SGTimeStamp::sleepForMSec(1);
}
}
void testBasicClone(HTTP::Client* cl)
{
std::unique_ptr<HTTPRepository> repo;
@@ -644,19 +618,9 @@ void testAbandonCorruptFiles(HTTP::Client* cl)
repo->setBaseUrl("http://localhost:2000/repo");
repo->update();
waitForUpdateComplete(cl, repo.get());
if (repo->failure() != HTTPRepository::REPO_PARTIAL_UPDATE) {
std::cerr << "Got failure state:" << repo->failure() << std::endl;
throw sg_exception("Bad result from corrupt files test");
}
auto failedFiles = repo->failures();
if (failedFiles.size() != 1) {
throw sg_exception("Bad result from corrupt files test");
}
if (failedFiles.front().path.utf8Str() != "dirB/subdirG/fileBGA") {
throw sg_exception("Bad path from corrupt files test:" +
failedFiles.front().path.utf8Str());
if (repo->failure() != HTTPRepository::REPO_ERROR_CHECKSUM) {
std::cerr << "Got failure state:" << repo->failure() << std::endl;
throw sg_exception("Bad result from corrupt files test");
}
repo.reset();
@@ -693,21 +657,15 @@ void testServerModifyDuringSync(HTTP::Client* cl)
repo.reset(new HTTPRepository(p, cl));
repo->setBaseUrl("http://localhost:2000/repo");
global_repo->findEntry("dirA/fileAA")->accessCallback =
[](const TestRepoEntry &r) {
std::cout << "Modifying sub-tree" << std::endl;
global_repo->findEntry("dirB/subdirA/fileBAC")->revision++;
global_repo->defineFile("dirB/subdirZ/fileBZA");
global_repo->findEntry("dirB/subdirB/fileBBB")->revision++;
};
global_repo->findEntry("dirA/fileAA")->accessCallback.reset(make_callback(&modifyBTree));
repo->update();
waitForUpdateComplete(cl, repo.get());
global_repo->findEntry("dirA/fileAA")->accessCallback = AccessCallback{};
global_repo->findEntry("dirA/fileAA")->accessCallback.reset();
if (repo->failure() != HTTPRepository::REPO_PARTIAL_UPDATE) {
throw sg_exception("Bad result from modify during sync test");
if (repo->failure() != HTTPRepository::REPO_ERROR_CHECKSUM) {
throw sg_exception("Bad result from modify during sync test");
}
std::cout << "Passed test modify server during sync" << std::endl;
@@ -797,103 +755,6 @@ void testCopyInstalledChildren(HTTP::Client* cl)
std::cout << "passed Copy installed children" << std::endl;
}
void testRetryAfterSocketFailure(HTTP::Client *cl) {
global_repo->clearRequestCounts();
global_repo->clearFailFlags();
std::unique_ptr<HTTPRepository> repo;
SGPath p(simgear::Dir::current().path());
p.append("http_repo_retry_after_socket_fail");
simgear::Dir pd(p);
if (pd.exists()) {
pd.removeChildren();
}
repo.reset(new HTTPRepository(p, cl));
repo->setBaseUrl("http://localhost:2000/repo");
int aaFailsRemaining = 2;
int subdirBAFailsRemaining = 2;
TestApi::setResponseDoneCallback(
cl, [&aaFailsRemaining, &subdirBAFailsRemaining](int curlResult,
HTTP::Request_ptr req) {
if (req->url() == "http://localhost:2000/repo/dirA/fileAA") {
if (aaFailsRemaining == 0)
return false;
--aaFailsRemaining;
TestApi::markRequestAsFailed(req, 56, "Simulated socket failure");
return true;
} else if (req->url() ==
"http://localhost:2000/repo/dirB/subdirA/.dirindex") {
if (subdirBAFailsRemaining == 0)
return false;
--subdirBAFailsRemaining;
TestApi::markRequestAsFailed(req, 56, "Simulated socket failure");
return true;
} else {
return false;
}
});
repo->update();
waitForUpdateComplete(cl, repo.get());
if (repo->failure() != HTTPRepository::REPO_NO_ERROR) {
throw sg_exception("Bad result from retry socket failure test");
}
verifyFileState(p, "dirA/fileAA");
verifyFileState(p, "dirB/subdirA/fileBAA");
verifyFileState(p, "dirB/subdirA/fileBAC");
verifyRequestCount("dirA/fileAA", 3);
verifyRequestCount("dirB/subdirA", 3);
verifyRequestCount("dirB/subdirA/fileBAC", 1);
}
void testPersistentSocketFailure(HTTP::Client *cl) {
global_repo->clearRequestCounts();
global_repo->clearFailFlags();
std::unique_ptr<HTTPRepository> repo;
SGPath p(simgear::Dir::current().path());
p.append("http_repo_persistent_socket_fail");
simgear::Dir pd(p);
if (pd.exists()) {
pd.removeChildren();
}
repo.reset(new HTTPRepository(p, cl));
repo->setBaseUrl("http://localhost:2000/repo");
TestApi::setResponseDoneCallback(
cl, [](int curlResult, HTTP::Request_ptr req) {
const auto url = req->url();
if (url.find("http://localhost:2000/repo/dirB") == 0) {
TestApi::markRequestAsFailed(req, 56, "Simulated socket failure");
return true;
}
return false;
});
repo->update();
waitForUpdateComplete(cl, repo.get());
if (repo->failure() != HTTPRepository::REPO_PARTIAL_UPDATE) {
throw sg_exception("Bad result from retry socket failure test");
}
verifyFileState(p, "dirA/fileAA");
verifyRequestCount("dirA/fileAA", 1);
verifyRequestCount("dirD/fileDA", 1);
verifyRequestCount("dirD/subdirDA/fileDAA", 1);
verifyRequestCount("dirD/subdirDB/fileDBA", 1);
}
int main(int argc, char* argv[])
{
sglog().setLogLevels( SG_ALL, SG_INFO );
@@ -939,8 +800,6 @@ int main(int argc, char* argv[])
cl.clearAllConnections();
testCopyInstalledChildren(&cl);
testRetryAfterSocketFailure(&cl);
testPersistentSocketFailure(&cl);
std::cout << "all tests passed ok" << std::endl;
return 0;

View File

@@ -537,13 +537,6 @@ SGGeodesy::advanceRadM(const SGGeoc& geoc, double course, double distance,
}
}
SGGeoc SGGeodesy::advanceDegM(const SGGeoc &geoc, double course,
double distance) {
SGGeoc result;
advanceRadM(geoc, course * SG_DEGREES_TO_RADIANS, distance, result);
return result;
}
double
SGGeodesy::courseRad(const SGGeoc& from, const SGGeoc& to)
{

View File

@@ -65,9 +65,6 @@ public:
// Geocentric course/distance computation
static void advanceRadM(const SGGeoc& geoc, double course, double distance,
SGGeoc& result);
static SGGeoc advanceDegM(const SGGeoc &geoc, double course, double distance);
static double courseRad(const SGGeoc& from, const SGGeoc& to);
static double distanceRad(const SGGeoc& from, const SGGeoc& to);
static double distanceM(const SGGeoc& from, const SGGeoc& to);

View File

@@ -49,11 +49,6 @@ ResourceManager* ResourceManager::instance()
return static_manager;
}
bool ResourceManager::haveInstance()
{
return static_manager != nullptr;
}
ResourceManager::~ResourceManager()
{
assert(this == static_manager);
@@ -61,15 +56,6 @@ ResourceManager::~ResourceManager()
std::for_each(_providers.begin(), _providers.end(),
[](ResourceProvider* p) { delete p; });
}
void ResourceManager::reset()
{
if (static_manager) {
delete static_manager;
static_manager = nullptr;
}
}
/**
* trivial provider using a fixed base path
*/
@@ -121,8 +107,6 @@ void ResourceManager::removeProvider(ResourceProvider* aProvider)
SG_LOG(SG_GENERAL, SG_DEV_ALERT, "unknown provider doing remove");
return;
}
_providers.erase(it);
}
SGPath ResourceManager::findPath(const std::string& aResource, SGPath aContext)

View File

@@ -45,12 +45,8 @@ public:
PRIORITY_HIGH = 1000
} Priority;
static ResourceManager* instance();
static bool haveInstance();
static void reset();
static ResourceManager* instance();
/**
* add a simple fixed resource location, to resolve against
*/

View File

@@ -44,7 +44,6 @@
#if defined(SG_WINDOWS)
# include <direct.h>
# include <sys/utime.h>
# include <Shlwapi.h>
#endif
#include "sg_path.hxx"
@@ -195,8 +194,7 @@ SGPath::SGPath(PermissionChecker validator)
_permission_checker(validator),
_cached(false),
_rwCached(false),
_cacheEnabled(true),
_existsCached(false)
_cacheEnabled(true)
{
}
@@ -207,8 +205,7 @@ SGPath::SGPath( const std::string& p, PermissionChecker validator )
_permission_checker(validator),
_cached(false),
_rwCached(false),
_cacheEnabled(true),
_existsCached(false)
_cacheEnabled(true)
{
fix();
}
@@ -233,8 +230,7 @@ SGPath::SGPath( const SGPath& p,
_permission_checker(validator),
_cached(false),
_rwCached(false),
_cacheEnabled(p._cacheEnabled),
_existsCached(false)
_cacheEnabled(p._cacheEnabled)
{
append(r);
fix();
@@ -514,19 +510,6 @@ void SGPath::checkAccess() const
bool SGPath::exists() const
{
#if defined(SG_WINDOWS)
// optimisation: _wstat is slow, eg for TerraSync
if (!_cached && !_existsCached) {
std::wstring w(wstr());
if ((path.length() > 1) && (path.back() == '/')) {
w.pop_back();
}
_existsCached = true;
_exists = PathFileExistsW(w.c_str());
return _exists;
}
#endif
validate();
return _exists;
}

View File

@@ -356,7 +356,6 @@ private:
mutable bool _exists : 1;
mutable bool _isDir : 1;
mutable bool _isFile : 1;
mutable bool _existsCached : 1; ///< only used on Windows
mutable time_t _modTime;
mutable size_t _size;
};

View File

@@ -267,38 +267,6 @@ namespace simgear {
return do_strip( s, BOTHSTRIP );
}
string makeStringSafeForPropertyName(const std::string& str)
{
// This function replaces all characters in 'str' that are not
// alphanumeric or '-'.
// TODO: make the function multibyte safe.
string res = str;
int index = 0;
for (char& c : res) {
if (!std::isalpha(c) && !std::isdigit(c) && c != '-') {
switch (c) {
case ' ':
case '\t':
case '\n':
case '\r':
case '_':
case '.':
case '/':
case '\\':
res[index] = '-';
break;
default:
res[index] = '_';
SG_LOG(SG_GENERAL, SG_WARN, "makeStringSafeForPropertyName: Modified '" << str << "' to '" << res << "'");
}
}
index++;
}
return res;
}
void
stripTrailingNewlines_inplace(string& s)
{

View File

@@ -75,9 +75,7 @@ namespace simgear {
std::string rstrip( const std::string& s );
std::string strip( const std::string& s );
std::string makeStringSafeForPropertyName(const std::string& str);
/**
/**
* Return a new string with any trailing \\r and \\n characters removed.
* Typically useful to clean a CR-terminated line obtained from
* std::getline() which, upon reading CRLF (\\r\\n), discards the Line

View File

@@ -737,11 +737,6 @@ void testDecodeHex()
SG_VERIFY(decoded == data1);
}
void test_makeStringSafeForPropertyName()
{
SG_CHECK_EQUAL(strutils::makeStringSafeForPropertyName(" ABC/01234\t:\\\"_*$"), "-ABC-01234-_-_-__");
}
int main(int argc, char* argv[])
{
test_strip();
@@ -766,7 +761,6 @@ int main(int argc, char* argv[])
test_formatGeod();
test_iequals();
testDecodeHex();
test_makeStringSafeForPropertyName();
return EXIT_SUCCESS;
}

View File

@@ -36,7 +36,7 @@ void naRuntimeError(naContext c, const char* fmt, ...)
void naRethrowError(naContext subc)
{
strncpy(subc->callParent->error, subc->error, sizeof(subc->callParent->error));
strncpy(subc->callParent->error, subc->error, sizeof(subc->error));
subc->callParent->dieArg = subc->dieArg;
longjmp(subc->callParent->jumpHandle, 1);
}

View File

@@ -21,7 +21,6 @@
#include "NasalString.hxx"
#include <cassert>
#include <stdexcept> // for std::runtime_error
namespace nasal
{

View File

@@ -7,7 +7,7 @@
#include "iolib.h"
static void ghostDestroy(void* g);
naGhostType naIOGhostType = { ghostDestroy, "iofile", NULL, NULL };
naGhostType naIOGhostType = { ghostDestroy, "iofile" };
static struct naIOGhost* ioghost(naRef r)
{

View File

@@ -472,7 +472,7 @@ static naRef f_sprintf(naContext c, naRef me, int argc, naRef* args)
} else {
arg = naNumValue(arg);
if(naIsNil(arg))
fout = dosprintf("nil");
fout = dosprintf(fstr, "nil");
else if(t=='d' || t=='i' || t=='c')
fout = dosprintf(fstr, (int)naNumValue(arg).num);
else if(t=='o' || t=='u' || t=='x' || t=='X')

View File

@@ -9,10 +9,10 @@
#include "code.h"
static void lockDestroy(void* lock) { naFreeLock(lock); }
static naGhostType LockType = { lockDestroy, NULL, NULL, NULL };
static naGhostType LockType = { lockDestroy };
static void semDestroy(void* sem) { naFreeSem(sem); }
static naGhostType SemType = { semDestroy, NULL, NULL, NULL };
static naGhostType SemType = { semDestroy };
typedef struct {
naContext ctx;

View File

@@ -595,17 +595,13 @@ void Catalog::setUserEnabled(bool b)
m_userEnabled = b;
SGPath disableMarkerFile = installRoot() / "_disabled_";
if (m_userEnabled == false) {
sg_ofstream of(disableMarkerFile, std::ios::trunc | std::ios::out);
of << "1" << std::flush; // touch the file
of.close();
if (m_userEnabled) {
sg_ofstream of(disableMarkerFile);
of << "1\n"; // touch the file
} else {
if (disableMarkerFile.exists()) {
const bool ok = disableMarkerFile.remove();
if (!ok) {
SG_LOG(SG_IO, SG_WARN, "Failed to remove catalog-disable marker file:" << disableMarkerFile);
}
bool ok = disableMarkerFile.remove();
if (!ok) {
SG_LOG(SG_GENERAL, SG_ALERT, "Failed to remove catalog-disable marker file:" << disableMarkerFile);
}
}
@@ -643,53 +639,16 @@ void Catalog::processAlternate(SGPropertyNode_ptr alt)
return;
}
// we have an alternate ID, and it's different from our ID, so let's
// we have an alternate ID, and it's differnt from our ID, so let's
// define a new catalog
if (!altId.empty()) {
// don't auto-re-add Catalogs the user has explicilty rmeoved, that would
// suck
const auto removedByUser = root()->explicitlyRemovedCatalogs();
auto it = std::find(removedByUser.begin(), removedByUser.end(), altId);
if (it != removedByUser.end()) {
SG_LOG(SG_GENERAL, SG_INFO, "Adding new catalog:" << altId << " as version alternate for " << id());
// new catalog being added
createFromUrl(root(), altUrl);
// and we can go idle now
changeStatus(Delegate::FAIL_VERSION);
return;
}
SG_LOG(SG_GENERAL, SG_WARN,
"Adding new catalog:" << altId << " as version alternate for "
<< id());
// new catalog being added
auto newCat = createFromUrl(root(), altUrl);
bool didRun = false;
newCat->m_migratedFrom = this;
auto migratePackagesCb = [didRun](Catalog *c) mutable {
// removing callbacks is awkward, so use this
// flag to only run once. (and hence, we need to be mutable)
if (didRun)
return;
if (c->status() == Delegate::STATUS_REFRESHED) {
didRun = true;
string_list existing;
for (const auto &pack : c->migratedFrom()->installedPackages()) {
existing.push_back(pack->id());
}
const int count = c->markPackagesForInstallation(existing);
SG_LOG(
SG_GENERAL, SG_INFO,
"Marked " << count
<< " packages from previous catalog for installation");
}
};
newCat->addStatusCallback(migratePackagesCb);
// and we can go idle now
changeStatus(Delegate::FAIL_VERSION);
return;
}
SG_LOG(SG_GENERAL, SG_INFO, "Migrating catalog " << id() << " to new URL:" << altUrl);
@@ -698,26 +657,6 @@ void Catalog::processAlternate(SGPropertyNode_ptr alt)
root()->makeHTTPRequest(dl);
}
int Catalog::markPackagesForInstallation(const string_list &packageIds) {
int result = 0;
for (const auto &id : packageIds) {
auto ourPkg = getPackageById(id);
if (!ourPkg)
continue;
auto existing = ourPkg->existingInstall();
if (!existing) {
ourPkg->markForInstall();
++result;
}
} // of outer package ID candidates iteration
return result;
}
CatalogRef Catalog::migratedFrom() const { return m_migratedFrom; }
} // of namespace pkg
} // of namespace simgear

View File

@@ -23,7 +23,6 @@
#include <map>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/props/props.hxx>
#include <simgear/structure/SGReferenced.hxx>
@@ -94,7 +93,7 @@ public:
/**
* retrieve all the packages in the catalog which are installed
* and have a pending update
* and have a pendig update
*/
PackageList packagesNeedingUpdate() const;
@@ -152,32 +151,7 @@ public:
bool isUserEnabled() const;
void setUserEnabled(bool b);
/**
* Given a list of package IDs, mark all which exist in this package,
* for installation. ANy packahe IDs not present in this catalog,
* will be ignored.
*
* @result The number for packages newly marked for installation.
*/
int markPackagesForInstallation(const string_list &packageIds);
/**
* When a catalog is added due to migration, this will contain the
* Catalog which triggered the add. Usually this will be a catalog
* corresponding to an earlier version.
*
* Note it's only valid at the time, the migration actually took place;
* when the new catalog is loaded from disk, this value will return
* null.
*
* This is intended to allow Uis to show a 'catalog was migrated'
* feedback, when they see a catalog refresh, which has a non-null
* value of this method.
*/
CatalogRef migratedFrom() const;
private:
private:
Catalog(Root* aRoot);
class Downloader;
@@ -223,8 +197,6 @@ public:
PackageWeakMap m_variantDict;
function_list<Callback> m_statusCallbacks;
CatalogRef m_migratedFrom;
};
} // of namespace pkg

View File

@@ -827,10 +827,7 @@ void testVersionMigrateToId(HTTP::Client* cl)
it = std::find(enabledCats.begin(), enabledCats.end(), altCat);
SG_VERIFY(it != enabledCats.end());
SG_CHECK_EQUAL(altCat->packagesNeedingUpdate().size(),
1); // should be the 737
// install a parallel package from the new catalog
pkg::PackageRef p2 = root->getPackageById("org.flightgear.test.catalog-alt.b737-NG");
SG_CHECK_EQUAL(p2->id(), "b737-NG");
@@ -843,8 +840,8 @@ void testVersionMigrateToId(HTTP::Client* cl)
pkg::PackageRef p3 = root->getPackageById("b737-NG");
SG_CHECK_EQUAL(p2, p3);
}
// test that re-init-ing doesn't migrate again
// test that re-init-ing doesn't mirgate again
{
pkg::RootRef root(new pkg::Root(rootPath, "7.5"));
root->setHTTPClient(cl);
@@ -1187,128 +1184,6 @@ void testMirrorsFailure(HTTP::Client* cl)
}
void testMigrateInstalled(HTTP::Client *cl) {
SGPath rootPath(simgear::Dir::current().path());
rootPath.append("pkg_migrate_installed");
simgear::Dir pd(rootPath);
pd.removeChildren();
pkg::RootRef root(new pkg::Root(rootPath, "8.1.2"));
root->setHTTPClient(cl);
pkg::CatalogRef oldCatalog, newCatalog;
{
oldCatalog = pkg::Catalog::createFromUrl(
root.ptr(), "http://localhost:2000/catalogTest1/catalog.xml");
waitForUpdateComplete(cl, root);
pkg::PackageRef p1 =
root->getPackageById("org.flightgear.test.catalog1.b747-400");
p1->install();
auto p2 = root->getPackageById("org.flightgear.test.catalog1.c172p");
p2->install();
auto p3 = root->getPackageById("org.flightgear.test.catalog1.b737-NG");
p3->install();
waitForUpdateComplete(cl, root);
}
{
newCatalog = pkg::Catalog::createFromUrl(
root.ptr(), "http://localhost:2000/catalogTest2/catalog.xml");
waitForUpdateComplete(cl, root);
string_list existing;
for (const auto &pack : oldCatalog->installedPackages()) {
existing.push_back(pack->id());
}
SG_CHECK_EQUAL(4, existing.size());
int result = newCatalog->markPackagesForInstallation(existing);
SG_CHECK_EQUAL(2, result);
SG_CHECK_EQUAL(2, newCatalog->packagesNeedingUpdate().size());
auto p1 = root->getPackageById("org.flightgear.test.catalog2.b737-NG");
auto ins = p1->existingInstall();
SG_CHECK_EQUAL(0, ins->revsion());
}
{
root->scheduleAllUpdates();
waitForUpdateComplete(cl, root);
SG_CHECK_EQUAL(0, newCatalog->packagesNeedingUpdate().size());
auto p1 = root->getPackageById("org.flightgear.test.catalog2.b737-NG");
auto ins = p1->existingInstall();
SG_CHECK_EQUAL(ins->revsion(), p1->revision());
}
}
void testDontMigrateRemoved(HTTP::Client *cl) {
global_catalogVersion = 2; // version which has migration info
SGPath rootPath(simgear::Dir::current().path());
rootPath.append("cat_dont_migrate_id");
simgear::Dir pd(rootPath);
pd.removeChildren();
// install and mnaully remove the alt catalog
{
pkg::RootRef root(new pkg::Root(rootPath, "8.1.2"));
root->setHTTPClient(cl);
pkg::CatalogRef c = pkg::Catalog::createFromUrl(
root.ptr(), "http://localhost:2000/catalogTest1/catalog-alt.xml");
waitForUpdateComplete(cl, root);
root->removeCatalogById("org.flightgear.test.catalog-alt");
}
// install the migration catalog
{
pkg::RootRef root(new pkg::Root(rootPath, "8.1.2"));
root->setHTTPClient(cl);
pkg::CatalogRef c = pkg::Catalog::createFromUrl(
root.ptr(), "http://localhost:2000/catalogTest1/catalog.xml");
waitForUpdateComplete(cl, root);
SG_VERIFY(c->isEnabled());
}
// change version to an alternate one
{
pkg::RootRef root(new pkg::Root(rootPath, "7.5"));
auto removed = root->explicitlyRemovedCatalogs();
auto j = std::find(removed.begin(), removed.end(),
"org.flightgear.test.catalog-alt");
SG_VERIFY(j != removed.end());
root->setHTTPClient(cl);
// this would tirgger migration, but we blocked it
root->refresh(true);
waitForUpdateComplete(cl, root);
pkg::CatalogRef cat = root->getCatalogById("org.flightgear.test.catalog1");
SG_VERIFY(!cat->isEnabled());
SG_CHECK_EQUAL(cat->status(), pkg::Delegate::FAIL_VERSION);
SG_CHECK_EQUAL(cat->id(), "org.flightgear.test.catalog1");
SG_CHECK_EQUAL(cat->url(),
"http://localhost:2000/catalogTest1/catalog.xml");
auto enabledCats = root->catalogs();
auto it = std::find(enabledCats.begin(), enabledCats.end(), cat);
SG_VERIFY(it == enabledCats.end());
// check the new catalog
auto altCat = root->getCatalogById("org.flightgear.test.catalog-alt");
SG_VERIFY(altCat.get() == nullptr);
}
}
int main(int argc, char* argv[])
{
sglog().setLogLevels( SG_ALL, SG_WARN );
@@ -1353,11 +1228,7 @@ int main(int argc, char* argv[])
testInstallBadPackage(&cl);
testMirrorsFailure(&cl);
testMigrateInstalled(&cl);
testDontMigrateRemoved(&cl);
cerr << "Successfully passed all tests!" << endl;
return EXIT_SUCCESS;
}

View File

@@ -34,7 +34,7 @@ namespace simgear {
namespace pkg {
Package::Package(const SGPropertyNode* aProps, CatalogRef aCatalog) :
m_catalog(aCatalog.get())
m_catalog(aCatalog)
{
initWithProps(aProps);
}
@@ -198,11 +198,6 @@ InstallRef Package::install()
{
InstallRef ins = existingInstall();
if (ins) {
// if there's updates, treat this as a 'start update' request
if (ins->hasUpdate()) {
m_catalog->root()->scheduleToUpdate(ins);
}
return ins;
}
@@ -215,39 +210,13 @@ InstallRef Package::install()
return ins;
}
InstallRef Package::markForInstall() {
InstallRef ins = existingInstall();
if (ins) {
return ins;
}
const auto pd = pathOnDisk();
Dir dir(pd);
if (!dir.create(0700)) {
SG_LOG(SG_IO, SG_ALERT,
"Package::markForInstall: couldn't create directory at:" << pd);
return {};
}
ins = new Install{this, pd};
_install_cb(this, ins); // not sure if we should trigger the callback for this
// repeat for dependencies to be kind
for (auto dep : dependencies()) {
dep->markForInstall();
}
return ins;
}
InstallRef Package::existingInstall(const InstallCallback& cb) const
{
InstallRef install;
try {
install = m_catalog->root()->existingInstallForPackage(const_cast<Package*>(this));
} catch (std::exception& ) {
return {};
return InstallRef();
}
if( cb )
@@ -266,11 +235,6 @@ std::string Package::id() const
return m_id;
}
CatalogRef Package::catalog() const
{
return {m_catalog};
}
std::string Package::qualifiedId() const
{
return m_catalog->id() + "." + id();

View File

@@ -61,13 +61,6 @@ public:
InstallRef
existingInstall(const InstallCallback& cb = InstallCallback()) const;
/**
* Mark this package for installation, but don't actually start the
* download process. This creates the on-disk placeholder, so
* the package will appear an eededing to be updated.
*/
InstallRef markForInstall();
bool isInstalled() const;
/**
@@ -137,7 +130,8 @@ public:
size_t fileSizeBytes() const;
CatalogRef catalog() const;
CatalogRef catalog() const
{ return m_catalog; }
bool matches(const SGPropertyNode* aFilter) const;
@@ -242,7 +236,7 @@ private:
SGPropertyNode_ptr m_props;
std::string m_id;
string_set m_tags;
Catalog* m_catalog = nullptr; // non-owning ref, explicitly
CatalogRef m_catalog;
string_list m_variants;
mutable function_list<InstallCallback> _install_cb;

View File

@@ -283,32 +283,6 @@ public:
fireDataForThumbnail(url, reinterpret_cast<const uint8_t*>(bytes.data()), bytes.size());
}
void writeRemovedCatalogsFile() const {
SGPath p = path / "RemovedCatalogs";
sg_ofstream stream(p, std::ios::out | std::ios::trunc | std::ios::binary);
for (const auto &cid : manuallyRemovedCatalogs) {
stream << cid << "\n";
}
stream.close();
}
void loadRemovedCatalogsFile() {
manuallyRemovedCatalogs.clear();
SGPath p = path / "RemovedCatalogs";
if (!p.exists())
return;
sg_ifstream stream(p, std::ios::in);
while (!stream.eof()) {
std::string line;
std::getline(stream, line);
const auto trimmed = strutils::strip(line);
if (!trimmed.empty()) {
manuallyRemovedCatalogs.push_back(trimmed);
}
} // of lines iteration
}
DelegateVec delegates;
SGPath path;
@@ -338,9 +312,6 @@ public:
typedef std::map<PackageRef, InstallRef> InstallCache;
InstallCache m_installs;
/// persistent list of catalogs the user has manually removed
string_list manuallyRemovedCatalogs;
};
@@ -429,8 +400,6 @@ Root::Root(const SGPath& aPath, const std::string& aVersion) :
thumbsCacheDir.create(0755);
}
d->loadRemovedCatalogsFile();
for (SGPath c : dir.children(Dir::TYPE_DIR | Dir::NO_DOT_OR_DOTDOT)) {
// note this will set the catalog status, which will insert into
// disabled catalogs automatically if necesary
@@ -652,13 +621,6 @@ void Root::scheduleToUpdate(InstallRef aInstall)
}
}
void Root::scheduleAllUpdates() {
auto toBeUpdated = packagesNeedingUpdate(); // make a copy
for (const auto &u : toBeUpdated) {
scheduleToUpdate(u->existingInstall());
}
}
bool Root::isInstallQueued(InstallRef aInstall) const
{
auto it = std::find(d->updateDeque.begin(), d->updateDeque.end(), aInstall);
@@ -728,17 +690,14 @@ void Root::catalogRefreshStatus(CatalogRef aCat, Delegate::StatusCode aReason)
d->refreshing.erase(aCat);
}
if (aCat->isUserEnabled() &&
(aReason == Delegate::STATUS_REFRESHED) &&
(catIt == d->catalogs.end()))
{
if ((aReason == Delegate::STATUS_REFRESHED) && (catIt == d->catalogs.end())) {
assert(!aCat->id().empty());
d->catalogs.insert(catIt, CatalogDict::value_type(aCat->id(), aCat));
// catalog might have been previously disabled, let's remove in that case
// catalog might have been previously disabled, let's remove in that case
auto j = std::find(d->disabledCatalogs.begin(),
d->disabledCatalogs.end(),
aCat);
d->disabledCatalogs.end(),
aCat);
if (j != d->disabledCatalogs.end()) {
SG_LOG(SG_GENERAL, SG_INFO, "re-enabling disabled catalog:" << aCat->id());
d->disabledCatalogs.erase(j);
@@ -746,7 +705,7 @@ void Root::catalogRefreshStatus(CatalogRef aCat, Delegate::StatusCode aReason)
}
if (!aCat->isEnabled()) {
// catalog has errors or was disabled by user, disable it
// catalog has errors, disable it
auto j = std::find(d->disabledCatalogs.begin(),
d->disabledCatalogs.end(),
aCat);
@@ -821,9 +780,6 @@ bool Root::removeCatalogById(const std::string& aId)
<< "failed to remove directory");
}
d->manuallyRemovedCatalogs.push_back(aId);
d->writeRemovedCatalogsFile();
// notify that a catalog is being removed
d->firePackagesChanged();
@@ -895,10 +851,6 @@ void Root::unregisterInstall(InstallRef ins)
d->fireFinishUninstall(ins->package());
}
string_list Root::explicitlyRemovedCatalogs() const {
return d->manuallyRemovedCatalogs;
}
} // of namespace pkg
} // of namespace simgear

View File

@@ -155,22 +155,7 @@ public:
void requestThumbnailData(const std::string& aUrl);
bool isInstallQueued(InstallRef aInstall) const;
/**
* Mark all 'to be updated' packages for update now
*/
void scheduleAllUpdates();
/**
* @brief list of catalog IDs, the user has explicitly removed via
* removeCatalogById(). This is important to allow the user to opt-out
* of migrated packages.
*
* This information is stored in a helper file, in the root directory
*/
string_list explicitlyRemovedCatalogs() const;
private:
private:
friend class Install;
friend class Catalog;
friend class Package;

View File

@@ -1,86 +0,0 @@
<?xml version="1.0"?>
<PropertyList>
<id>org.flightgear.test.catalog2</id>
<description>Second test catalog</description>
<url>http://localhost:2000/catalogTest2/catalog.xml</url>
<catalog-version>4</catalog-version>
<version>8.1.*</version>
<version>8.0.0</version>
<version>8.2.0</version>
<package>
<id>alpha</id>
<name>Alpha package</name>
<revision type="int">8</revision>
<file-size-bytes type="int">593</file-size-bytes>
<md5>a469c4b837f0521db48616cfe65ac1ea</md5>
<url>http://localhost:2000/catalogTest1/alpha.zip</url>
<dir>alpha</dir>
</package>
<package>
<id>b737-NG</id>
<name>Boeing 737 NG</name>
<dir>b737NG</dir>
<description>A popular twin-engined narrow body jet</description>
<revision type="int">111</revision>
<file-size-bytes type="int">860</file-size-bytes>
<tag>boeing</tag>
<tag>jet</tag>
<tag>ifr</tag>
<!-- not within a localized element -->
<de>
<description>German description of B737NG XYZ</description>
</de>
<fr>
<description>French description of B737NG</description>
</fr>
<rating>
<FDM type="int">5</FDM>
<systems type="int">5</systems>
<model type="int">4</model>
<cockpit type="int">4</cockpit>
</rating>
<md5>a94ca5704f305b90767f40617d194ed6</md5>
<url>http://localhost:2000/mirrorA/b737.tar.gz</url>
<url>http://localhost:2000/mirrorB/b737.tar.gz</url>
<url>http://localhost:2000/mirrorC/b737.tar.gz</url>
</package>
<package>
<id>b747-400</id>
<name>Boeing 747-400</name>
<dir>b744</dir>
<description>A popular four-engined wide-body jet</description>
<revision type="int">111</revision>
<file-size-bytes type="int">860</file-size-bytes>
<tag>boeing</tag>
<tag>jet</tag>
<tag>ifr</tag>
<rating>
<FDM type="int">5</FDM>
<systems type="int">5</systems>
<model type="int">4</model>
<cockpit type="int">4</cockpit>
</rating>
<md5>4d3f7417d74f811aa20ccc4f35673d20</md5>
<!-- this URL will sometimes fail, on purpose -->
<url>http://localhost:2000/catalogTest1/b747.tar.gz</url>
</package>
</PropertyList>

View File

@@ -48,18 +48,6 @@ using std::stringstream;
using namespace simgear;
struct SGPropertyNodeListeners
{
/* This keeps a count of the current number of nested invocations of
forEachListener(). If non-zero, other code higher up the stack is iterating
_items[] so for example code must not erase items in the vector. */
int _num_iterators = 0;
std::vector<SGPropertyChangeListener *> _items;
};
////////////////////////////////////////////////////////////////////////
// Local classes.
////////////////////////////////////////////////////////////////////////
@@ -980,7 +968,7 @@ SGPropertyNode::~SGPropertyNode ()
if (_listeners) {
vector<SGPropertyChangeListener*>::iterator it;
for (it = _listeners->_items.begin(); it != _listeners->_items.end(); ++it)
for (it = _listeners->begin(); it != _listeners->end(); ++it)
(*it)->unregister_property(this);
delete _listeners;
}
@@ -2404,21 +2392,8 @@ SGPropertyNode::addChangeListener (SGPropertyChangeListener * listener,
bool initial)
{
if (_listeners == 0)
_listeners = new SGPropertyNodeListeners;
/* If there's a nullptr entry (a listener that was unregistered), we
overwrite it. This ensures that listeners that routinely unregister+register
themselves don't make _listeners->_items grow unnecessarily. Otherwise simply
append. */
auto it = std::find(_listeners->_items.begin(), _listeners->_items.end(),
(SGPropertyChangeListener*) nullptr
);
if (it == _listeners->_items.end()) {
_listeners->_items.push_back(listener);
}
else {
*it = listener;
}
_listeners = new vector<SGPropertyChangeListener*>;
_listeners->push_back(listener);
listener->register_property(this);
if (initial)
listener->valueChanged(this);
@@ -2430,29 +2405,14 @@ SGPropertyNode::removeChangeListener (SGPropertyChangeListener * listener)
if (_listeners == 0)
return;
vector<SGPropertyChangeListener*>::iterator it =
find(_listeners->_items.begin(), _listeners->_items.end(), listener);
if (it != _listeners->_items.end()) {
if (_listeners->_num_iterators) {
/* _listeners._items is currently being iterated further up the stack in
this thread by one or more nested invocations of forEachListener(), so
we must be careful to not break these iterators. So we must not delete
_listeners. And we must not erase this entry from the vector because that
could cause the next listener to be missed out.
So we simply set this entry to nullptr (nullptr items are skipped by
forEachListener()). When all invocations of forEachListener() have
finished iterating and it is safe to modify things again, it will clean
up any nullptr entries in _listeners->_items. */
*it = nullptr;
listener->unregister_property(this);
}
else {
_listeners->_items.erase(it);
listener->unregister_property(this);
if (_listeners->_items.empty()) {
delete _listeners;
_listeners = 0;
}
find(_listeners->begin(), _listeners->end(), listener);
if (it != _listeners->end()) {
_listeners->erase(it);
listener->unregister_property(this);
if (_listeners->empty()) {
vector<SGPropertyChangeListener*>* tmp = _listeners;
_listeners = 0;
delete tmp;
}
}
}
@@ -2501,67 +2461,15 @@ SGPropertyNode::fireChildrenRemovedRecursive()
}
}
/* Calls <callback> for each item in _listeners. We are careful to skip nullptr
entries in _listeners->items[], which can be created if listeners are removed
while we are iterating. */
static void forEachListener(
SGPropertyNode* node,
SGPropertyNodeListeners*& _listeners,
std::function<void (SGPropertyChangeListener*)> callback
)
{
if (!_listeners) return;
_listeners->_num_iterators += 1;
/* We need to use an index here when iterating _listeners->_items, not an
iterator. This is because a listener may add new listeners, causing the
vector to be reallocated, which would invalidate any iterator. */
for (size_t i = 0; i < _listeners->_items.size(); ++i) {
auto listener = _listeners->_items[i];
if (listener) {
try {
callback(listener);
}
catch (std::exception& e) {
SG_LOG(SG_GENERAL, SG_ALERT, "Ignoring exception from property callback: " << e.what());
}
}
}
_listeners->_num_iterators -= 1;
if (_listeners->_num_iterators == 0) {
/* Remove any items that have been set to nullptr. */
_listeners->_items.erase(
std::remove(_listeners->_items.begin(), _listeners->_items.end(), (SGPropertyChangeListener*) nullptr),
_listeners->_items.end()
);
if (_listeners->_items.empty()) {
delete _listeners;
_listeners = nullptr;
}
}
}
int SGPropertyNode::nListeners() const
{
if (!_listeners) return 0;
int n = 0;
for (auto listener: _listeners->_items) {
if (listener) n += 1;
}
return n;
}
void
SGPropertyNode::fireValueChanged (SGPropertyNode * node)
{
forEachListener(
node,
_listeners,
[&](SGPropertyChangeListener* listener) { listener->valueChanged(node);}
);
if (_listeners != 0) {
for (unsigned int i = 0; i < _listeners->size(); i++) {
if ((*_listeners)[i])
(*_listeners)[i]->valueChanged(node);
}
}
if (_parent != 0)
_parent->fireValueChanged(node);
}
@@ -2570,11 +2478,11 @@ void
SGPropertyNode::fireChildAdded (SGPropertyNode * parent,
SGPropertyNode * child)
{
forEachListener(
parent,
_listeners,
[&](SGPropertyChangeListener* listener) { listener->childAdded(parent, child);}
);
if (_listeners != 0) {
for (unsigned int i = 0; i < _listeners->size(); i++) {
(*_listeners)[i]->childAdded(parent, child);
}
}
if (_parent != 0)
_parent->fireChildAdded(parent, child);
}
@@ -2583,11 +2491,11 @@ void
SGPropertyNode::fireChildRemoved (SGPropertyNode * parent,
SGPropertyNode * child)
{
forEachListener(
parent,
_listeners,
[&](SGPropertyChangeListener* listener) { listener->childRemoved(parent, child);}
);
if (_listeners != 0) {
for (unsigned int i = 0; i < _listeners->size(); i++) {
(*_listeners)[i]->childRemoved(parent, child);
}
}
if (_parent != 0)
_parent->fireChildRemoved(parent, child);
}

View File

@@ -759,9 +759,6 @@ private:
};
struct SGPropertyNodeListeners;
/**
* A node in a property tree.
*/
@@ -1716,7 +1713,7 @@ public:
/**
* Get the number of listeners.
*/
int nListeners () const;
int nListeners () const { return _listeners ? (int)_listeners->size() : 0; }
/**
@@ -1850,7 +1847,7 @@ private:
char * string_val;
} _local_val;
SGPropertyNodeListeners* _listeners;
std::vector<SGPropertyChangeListener *> * _listeners;
// Pass name as a pair of iterators
template<typename Itr>
@@ -2197,12 +2194,11 @@ public:
_property->addChangeListener(this,initial);
}
SGPropertyChangeCallback(const SGPropertyChangeCallback<T>& other)
: SGPropertyChangeListener(other),
_obj(other._obj), _callback(other._callback), _property(other._property)
{
_property->addChangeListener(this,false);
}
SGPropertyChangeCallback(const SGPropertyChangeCallback<T>& other) :
_obj(other._obj), _callback(other._callback), _property(other._property)
{
_property->addChangeListener(this,false);
}
virtual ~SGPropertyChangeCallback()
{

View File

@@ -854,48 +854,4 @@ copyPropertiesWithAttribute(const SGPropertyNode *in, SGPropertyNode *out,
return true;
}
namespace {
// for normal recursion we want to call the predicate *before* creating children
bool _inner_copyPropertiesIf(const SGPropertyNode *in, SGPropertyNode *out,
PropertyPredicate predicate)
{
bool retval = copyPropertyValue(in, out);
if (!retval) {
return false;
}
out->setAttributes( in->getAttributes() );
int nChildren = in->nChildren();
for (int i = 0; i < nChildren; i++) {
const SGPropertyNode* in_child = in->getChild(i);
// skip this child
if (!predicate(in_child)) {
continue;
}
SGPropertyNode* out_child = out->getChild(in_child->getNameString(),
in_child->getIndex(),
true);
bool ok = copyPropertiesIf(in_child, out_child, predicate);
if (!ok) {
return false;
}
} // of children iteration
return true;
}
} // of anonymous namespace
bool copyPropertiesIf(const SGPropertyNode *in, SGPropertyNode *out,
PropertyPredicate predicate)
{
// allow the *entire* copy to be a no-op
bool doCopy = predicate(in);
if (!doCopy)
return true; // doesn't count as failure
return _inner_copyPropertiesIf(in, out, predicate);
}
// end of props_io.cxx

View File

@@ -17,7 +17,6 @@
#include <string>
#include <iosfwd>
#include <functional>
/**
* Read properties from an XML input stream.
@@ -68,16 +67,6 @@ bool copyProperties (const SGPropertyNode *in, SGPropertyNode *out);
bool copyPropertiesWithAttribute(const SGPropertyNode *in, SGPropertyNode *out,
SGPropertyNode::Attribute attr);
using PropertyPredicate = std::function<bool (const SGPropertyNode* in)>;
/**
* Copy properties, if the predicate returns true for the in node.
* If a parent node returns false, descendats will <em>not</em> be
* checked
*/
bool copyPropertiesIf(const SGPropertyNode *in, SGPropertyNode *out,
PropertyPredicate predicate);
#endif // __PROPS_IO_HXX
// end of props_io.hxx

View File

@@ -451,17 +451,13 @@ void defineSamplePropertyTree(SGPropertyNode_ptr root)
class TestListener : public SGPropertyChangeListener
{
public:
TestListener(SGPropertyNode* root, bool recursive = false, bool self_unregister = false) :
_root(root), _self_unregister(self_unregister) {}
TestListener(SGPropertyNode* root, bool recursive = false) :
_root(root) {}
virtual void valueChanged(SGPropertyNode* node) override
{
std::cout << "saw value changed for:" << node->getPath() << std::endl;
valueChangeCount[node]++;
if (_self_unregister) {
std::cout << "valueChanged(): calling removeChangeListener() to self-remove\n";
node->removeChangeListener(this);
}
}
int checkValueChangeCount(const std::string& path) const
@@ -537,7 +533,6 @@ public:
}
private:
SGPropertyNode* _root;
bool _self_unregister;
std::map<SGPropertyNode*, unsigned int> valueChangeCount;
std::vector<ParentChange> adds;
@@ -947,13 +942,6 @@ void testDeleterListener()
SG_VERIFY(ensureNListeners(tree, 0));
}
// Self-unregister. prior to 2019-07-08 this would segv.
{
std::shared_ptr<TestListener> l1( new TestListener(tree.get(), true /* recursive */, true /*self_unregister*/));
tree->setFloatValue("position/body/sub/self-unregister", 0.1);
tree->getNode("position/body/sub/self-unregister")->addChangeListener(l1.get());
tree->setFloatValue("position/body/sub/self-unregister", 0.2);
}
}
int main (int ac, char ** av)

View File

@@ -28,7 +28,7 @@
#include <simgear/compiler.h>
#include <map>
#include <mutex>
#include <osg/AlphaFunc>
#include <osg/Group>
@@ -103,39 +103,37 @@ SGMatModel::get_model_count( SGPropertyNode *prop_root )
inline void
SGMatModel::load_models( SGPropertyNode *prop_root )
{
std::lock_guard<std::mutex> g(_loadMutex);
// Load model only on demand
if (!_models_loaded) {
for (unsigned int i = 0; i < _paths.size(); i++) {
osg::Node* entity = SGModelLib::loadModel(_paths[i], prop_root);
if (entity != 0) {
// FIXME: this stuff can be handled
// in the XML wrapper as well (at least,
// the billboarding should be handled
// there).
if (_heading_type == HEADING_BILLBOARD) {
// if the model is a billboard, it is likely :
// 1. a branch with only leaves,
// 2. a tree or a non rectangular shape faked by transparency
// We add alpha clamp then
osg::StateSet* stateSet = entity->getOrCreateStateSet();
osg::AlphaFunc* alphaFunc =
new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.01f);
stateSet->setAttributeAndModes(alphaFunc,
osg::StateAttribute::OVERRIDE);
stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
}
_models.push_back(entity);
} else {
SG_LOG(SG_INPUT, SG_ALERT, "Failed to load object " << _paths[i]);
// Ensure the vector contains something, otherwise get_random_model below fails
_models.push_back(new osg::Node());
}
}
// Load model only on demand
if (!_models_loaded) {
for (unsigned int i = 0; i < _paths.size(); i++) {
osg::Node *entity = SGModelLib::loadModel(_paths[i], prop_root);
if (entity != 0) {
// FIXME: this stuff can be handled
// in the XML wrapper as well (at least,
// the billboarding should be handled
// there).
if (_heading_type == HEADING_BILLBOARD) {
// if the model is a billboard, it is likely :
// 1. a branch with only leaves,
// 2. a tree or a non rectangular shape faked by transparency
// We add alpha clamp then
osg::StateSet* stateSet = entity->getOrCreateStateSet();
osg::AlphaFunc* alphaFunc =
new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.01f);
stateSet->setAttributeAndModes(alphaFunc,
osg::StateAttribute::OVERRIDE);
stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
}
_models.push_back(entity);
} else {
SG_LOG(SG_INPUT, SG_ALERT, "Failed to load object " << _paths[i]);
// Ensure the vector contains something, otherwise get_random_model below fails
_models.push_back(new osg::Node());
}
}
}
_models_loaded = true;
}

View File

@@ -149,8 +149,6 @@ private:
double _spacing_m;
double _range_m;
HeadingType _heading_type;
std::mutex _loadMutex;
};

View File

@@ -82,13 +82,13 @@ void _writeColor(GLenum pixelFormat, T* data, float scale, osg::Vec4 value)
switch(pixelFormat)
{
case(GL_DEPTH_COMPONENT): //intentionally fall through and execute the code for GL_LUMINANCE
case(GL_LUMINANCE): { *data = value.r()*scale; break; }
case(GL_ALPHA): { *data = value.a()*scale; break; }
case(GL_LUMINANCE_ALPHA): { *data++ = value.r()*scale; *data = value.a()*scale; break; }
case(GL_RGB): { *data++ = value.r()*scale; *data++ = value.g()*scale; *data = value.b()*scale; break; }
case(GL_RGBA): { *data++ = value.r()*scale; *data++ = value.g()*scale; *data++ = value.b()*scale; *data = value.a()*scale; break; }
case(GL_BGR): { *data++ = value.b()*scale; *data++ = value.g()*scale; *data = value.r()*scale; break; }
case(GL_BGRA): { *data++ = value.b()*scale; *data++ = value.g()*scale; *data++ = value.r()*scale; *data = value.a()*scale; break; }
case(GL_LUMINANCE): { *data++ = value.r()*scale; break; }
case(GL_ALPHA): { *data++ = value.a()*scale; break; }
case(GL_LUMINANCE_ALPHA): { *data++ = value.r()*scale; *data++ = value.a()*scale; break; }
case(GL_RGB): { *data++ = value.r()*scale; *data++ = value.g()*scale; *data++ = value.b()*scale; break; }
case(GL_RGBA): { *data++ = value.r()*scale; *data++ = value.g()*scale; *data++ = value.b()*scale; *data++ = value.a()*scale; break; }
case(GL_BGR): { *data++ = value.b()*scale; *data++ = value.g()*scale; *data++ = value.r()*scale; break; }
case(GL_BGRA): { *data++ = value.b()*scale; *data++ = value.g()*scale; *data++ = value.r()*scale; *data++ = value.a()*scale; break; }
}
}
@@ -265,13 +265,7 @@ osg::Image* computeMipmap( osg::Image* image, MipMapTuple attrs )
{
bool computeMipmap = false;
unsigned int nbComponents = osg::Image::computeNumComponents( image->getPixelFormat() );
int s = image->s();
int t = image->t();
if ( (s & (s - 1)) || (t & (t - 1)) ) // power of two test
{
SG_LOG(SG_IO, SG_DEV_ALERT, "mipmapping: texture size not a power-of-two: " + image->getFileName());
}
else if ( std::get<0>(attrs) != AUTOMATIC &&
if ( std::get<0>(attrs) != AUTOMATIC &&
( std::get<1>(attrs) != AUTOMATIC || nbComponents < 2 ) &&
( std::get<2>(attrs) != AUTOMATIC || nbComponents < 3 ) &&
( std::get<3>(attrs) != AUTOMATIC || nbComponents < 4 ) )
@@ -289,7 +283,9 @@ osg::Image* computeMipmap( osg::Image* image, MipMapTuple attrs )
if ( computeMipmap )
{
osg::ref_ptr<osg::Image> mipmaps = new osg::Image();
int r = image->r();
int s = image->s(),
t = image->t(),
r = image->r();
int nb = osg::Image::computeNumberOfMipmapLevels(s, t, r);
osg::Image::MipmapDataType mipmapOffsets;
unsigned int offset = 0;

View File

@@ -359,8 +359,6 @@ ModelRegistry::readImage(const string& fileName,
if (res.validImage()) {
osg::ref_ptr<osg::Image> srcImage = res.getImage();
int width = srcImage->s();
//int packing = srcImage->getPacking();
//printf("packing %d format %x pixel size %d InternalTextureFormat %x\n", packing, srcImage->getPixelFormat(), srcImage->getPixelSizeInBits(), srcImage->getInternalTextureFormat() );
bool transparent = srcImage->isImageTranslucent();
bool isNormalMap = false;
bool isEffect = false;
@@ -369,11 +367,6 @@ ModelRegistry::readImage(const string& fileName,
*/
bool can_compress = (transparent && compress_transparent) || (!transparent && compress_solid);
if (srcImage->getPixelSizeInBits() <= 16) {
SG_LOG(SG_IO, SG_INFO, "Ignoring " + absFileName + " for inclusion into the texture cache because pixel density too low at " << srcImage->getPixelSizeInBits() << " bits per pixek");
can_compress = false;
}
int height = srcImage->t();
// use the new file origin to determine any special processing

View File

@@ -490,8 +490,7 @@ sgLoad3DModel_internal(const SGPath& path,
}
}
auto particlesManager = ParticlesGlobalManager::instance();
if (particlesManager->isEnabled()) { //dbOptions->getPluginStringData("SimGear::PARTICLESYSTEM") != "OFF") {
if (GlobalParticleCallback::getEnabled()){//dbOptions->getPluginStringData("SimGear::PARTICLESYSTEM") != "OFF") {
std::vector<SGPropertyNode_ptr> particle_nodes;
particle_nodes = props->getChildren("particlesystem");
for (unsigned i = 0; i < particle_nodes.size(); ++i) {
@@ -504,9 +503,9 @@ sgLoad3DModel_internal(const SGPath& path,
options2->setDatabasePath(texturepath.utf8Str());
}
group->addChild(particlesManager->appendParticles(particle_nodes[i],
prop_root,
options2.get()));
group->addChild(Particles::appendParticles(particle_nodes[i],
prop_root,
options2.get()));
}
}

View File

@@ -17,12 +17,9 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include "particles.hxx"
#include <mutex>
#ifdef HAVE_CONFIG_H
# include <simgear_config.h>
#endif
#include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx>
@@ -45,122 +42,53 @@
#include <osg/MatrixTransform>
#include <osg/Node>
#include <simgear/scene/model/animation.hxx>
#include "particles.hxx"
namespace simgear
{
class ParticlesGlobalManager::ParticlesGlobalManagerPrivate : public osg::NodeCallback
void GlobalParticleCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
{
public:
ParticlesGlobalManagerPrivate() : _updater(new osgParticle::ParticleSystemUpdater),
_commonGeode(new osg::Geode)
{
}
enabled = !enabledNode || enabledNode->getBoolValue();
if (!enabled)
return;
SGQuatd q
= SGQuatd::fromLonLatDeg(modelRoot->getFloatValue("/position/longitude-deg",0),
modelRoot->getFloatValue("/position/latitude-deg",0));
osg::Matrix om(toOsg(q));
osg::Vec3 v(0,0,9.81);
gravity = om.preMult(v);
// NOTE: THIS WIND COMPUTATION DOESN'T SEEM TO AFFECT PARTICLES
const osg::Vec3& zUpWind = Particles::getWindVector();
osg::Vec3 w(zUpWind.y(), zUpWind.x(), -zUpWind.z());
wind = om.preMult(w);
void operator()(osg::Node* node, osg::NodeVisitor* nv) override
{
std::lock_guard<std::mutex> g(_lock);
_enabled = !_enabledNode || _enabledNode->getBoolValue();
if (!_enabled)
return;
const auto q = SGQuatd::fromLonLatDeg(_longitudeNode->getFloatValue(), _latitudeNode->getFloatValue());
osg::Matrix om(toOsg(q));
osg::Vec3 v(0, 0, 9.81);
_gravity = om.preMult(v);
// NOTE: THIS WIND COMPUTATION DOESN'T SEEM TO AFFECT PARTICLES
// const osg::Vec3& zUpWind = _wind;
// osg::Vec3 w(zUpWind.y(), zUpWind.x(), -zUpWind.z());
// _localWind = om.preMult(w);
}
// only call this with the lock held!
osg::Group* internalGetCommonRoot()
{
if (!_commonRoot.valid()) {
SG_LOG(SG_PARTICLES, SG_DEBUG, "Particle common root called.");
_commonRoot = new osg::Group;
_commonRoot->setName("common particle system root");
_commonGeode->setName("common particle system geode");
_commonRoot->addChild(_commonGeode);
_commonRoot->addChild(_updater);
_commonRoot->setNodeMask(~simgear::MODELLIGHT_BIT);
}
return _commonRoot.get();
}
std::mutex _lock;
bool _frozen = false;
osg::ref_ptr<osgParticle::ParticleSystemUpdater> _updater;
osg::ref_ptr<osg::Group> _commonRoot;
osg::ref_ptr<osg::Geode> _commonGeode;
osg::Vec3 _wind;
bool _globalCallbackRegistered = false;
bool _enabled = true;
osg::Vec3 _gravity;
// osg::Vec3 _localWind;
SGConstPropertyNode_ptr _enabledNode;
SGConstPropertyNode_ptr _longitudeNode, _latitudeNode;
};
static std::mutex static_managerLock;
static std::unique_ptr<ParticlesGlobalManager> static_instance;
ParticlesGlobalManager* ParticlesGlobalManager::instance()
{
std::lock_guard<std::mutex> g(static_managerLock);
if (!static_instance) {
static_instance.reset(new ParticlesGlobalManager);
}
return static_instance.get();
// SG_LOG(SG_PARTICLES, SG_ALERT,
// "wind vector:" << w[0] << "," <<w[1] << "," << w[2]);
}
void ParticlesGlobalManager::clear()
{
std::lock_guard<std::mutex> g(static_managerLock);
static_instance.reset();
}
ParticlesGlobalManager::ParticlesGlobalManager() : d(new ParticlesGlobalManagerPrivate)
{
}
//static members
osg::Vec3 GlobalParticleCallback::gravity;
osg::Vec3 GlobalParticleCallback::wind;
bool GlobalParticleCallback::enabled = true;
SGConstPropertyNode_ptr GlobalParticleCallback::enabledNode = 0;
ParticlesGlobalManager::~ParticlesGlobalManager()
{
if (d->_globalCallbackRegistered) {
// is this actually necessary? possibly not
d->_updater->setUpdateCallback(nullptr);
}
}
osg::ref_ptr<osg::Group> Particles::commonRoot;
osg::ref_ptr<osgParticle::ParticleSystemUpdater> Particles::psu = new osgParticle::ParticleSystemUpdater;
osg::ref_ptr<osg::Geode> Particles::commonGeode = new osg::Geode;
osg::Vec3 Particles::_wind;
bool Particles::_frozen = false;
bool ParticlesGlobalManager::isEnabled() const
Particles::Particles() :
useGravity(false),
useWind(false)
{
std::lock_guard<std::mutex> g(d->_lock);
return d->_enabled;
}
bool ParticlesGlobalManager::isFrozen() const
{
std::lock_guard<std::mutex> g(d->_lock);
return d->_frozen;
}
osg::Vec3 ParticlesGlobalManager::getWindVector() const
{
std::lock_guard<std::mutex> g(d->_lock);
return d->_wind;
}
template <typename Object>
class PointerGuard{
public:
PointerGuard() : _ptr(0) {}
Object* get() { return _ptr; }
Object* operator () ()
{
@@ -169,9 +97,24 @@ public:
return _ptr;
}
private:
Object* _ptr = nullptr;
Object* _ptr;
};
osg::Group* Particles::getCommonRoot()
{
if(!commonRoot.valid())
{
SG_LOG(SG_PARTICLES, SG_DEBUG, "Particle common root called.");
commonRoot = new osg::Group;
commonRoot.get()->setName("common particle system root");
commonGeode.get()->setName("common particle system geode");
commonRoot.get()->addChild(commonGeode.get());
commonRoot.get()->addChild(psu.get());
commonRoot->setNodeMask( ~simgear::MODELLIGHT_BIT );
}
return commonRoot.get();
}
void transformParticles(osgParticle::ParticleSystem* particleSys,
const osg::Matrix& mat)
{
@@ -186,182 +129,10 @@ void transformParticles(osgParticle::ParticleSystem* particleSys,
}
}
void Particles::operator()(osg::Node* node, osg::NodeVisitor* nv)
{
auto globalManager = ParticlesGlobalManager::instance();
//SG_LOG(SG_PARTICLES, SG_ALERT, "callback!\n");
particleSys->setFrozen(globalManager->isFrozen());
using namespace osg;
if (shooterValue)
shooter->setInitialSpeedRange(shooterValue->getValue(),
(shooterValue->getValue() + shooterExtraRange));
if (counterValue)
counter->setRateRange(counterValue->getValue(),
counterValue->getValue() + counterExtraRange);
else if (counterCond)
counter->setRateRange(counterStaticValue,
counterStaticValue + counterStaticExtraRange);
if (!globalManager->isEnabled() || (counterCond && !counterCond->test()))
counter->setRateRange(0, 0);
bool colorchange = false;
for (int i = 0; i < 8; ++i) {
if (colorComponents[i]) {
staticColorComponents[i] = colorComponents[i]->getValue();
colorchange = true;
}
}
if (colorchange)
particleSys->getDefaultParticleTemplate().setColorRange(osgParticle::rangev4(Vec4(staticColorComponents[0], staticColorComponents[1], staticColorComponents[2], staticColorComponents[3]), Vec4(staticColorComponents[4], staticColorComponents[5], staticColorComponents[6], staticColorComponents[7])));
if (startSizeValue)
startSize = startSizeValue->getValue();
if (endSizeValue)
endSize = endSizeValue->getValue();
if (startSizeValue || endSizeValue)
particleSys->getDefaultParticleTemplate().setSizeRange(osgParticle::rangef(startSize, endSize));
if (lifeValue)
particleSys->getDefaultParticleTemplate().setLifeTime(lifeValue->getValue());
if (particleFrame.valid()) {
MatrixList mlist = node->getWorldMatrices();
if (!mlist.empty()) {
const Matrix& particleMat = particleFrame->getMatrix();
Vec3d emitOrigin(mlist[0](3, 0), mlist[0](3, 1), mlist[0](3, 2));
Vec3d displace = emitOrigin - Vec3d(particleMat(3, 0), particleMat(3, 1),
particleMat(3, 2));
if (displace * displace > 10000.0 * 10000.0) {
// Make new frame for particle system, coincident with
// the emitter frame, but oriented with local Z.
SGGeod geod = SGGeod::fromCart(toSG(emitOrigin));
Matrix newParticleMat = makeZUpFrame(geod);
Matrix changeParticleFrame = particleMat * Matrix::inverse(newParticleMat);
particleFrame->setMatrix(newParticleMat);
transformParticles(particleSys.get(), changeParticleFrame);
}
}
}
if (program.valid() && useWind)
program->setWind(globalManager->getWindVector());
}
void Particles::setupShooterSpeedData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
shooterValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if (!shooterValue) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "Particles: shooter property error!\n");
}
shooterExtraRange = configNode->getFloatValue("extrarange", 0);
}
void Particles::setupCounterData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
counterValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if (!counterValue) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "counter property error!\n");
}
counterExtraRange = configNode->getFloatValue("extrarange", 0);
}
void Particles::Particles::setupCounterCondition(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
counterCond = sgReadCondition(modelRoot, configNode);
}
void Particles::setupCounterCondition(float aCounterStaticValue,
float aCounterStaticExtraRange)
{
counterStaticValue = aCounterStaticValue;
counterStaticExtraRange = aCounterStaticExtraRange;
}
void Particles::setupStartSizeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
startSizeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if (!startSizeValue) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "Particles: startSizeValue error!\n");
}
}
void Particles::setupEndSizeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
endSizeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if (!endSizeValue) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "Particles: startSizeValue error!\n");
}
}
void Particles::setupLifeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
lifeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if (!lifeValue) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "Particles: lifeValue error!\n");
}
}
void Particles::setupColorComponent(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot, int color,
int component)
{
SGSharedPtr<SGExpressiond> colorValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(),
SGLimitsd::max());
if (!colorValue) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "Particles: color property error!\n");
}
colorComponents[(color * 4) + component] = colorValue;
//number of color components = 4
}
void Particles::setupStaticColorComponent(float r1, float g1, float b1, float a1,
float r2, float g2, float b2, float a2)
{
staticColorComponents[0] = r1;
staticColorComponents[1] = g1;
staticColorComponents[2] = b1;
staticColorComponents[3] = a1;
staticColorComponents[4] = r2;
staticColorComponents[5] = g2;
staticColorComponents[6] = b2;
staticColorComponents[7] = a2;
}
void ParticlesGlobalManager::setWindVector(const osg::Vec3& wind)
{
std::lock_guard<std::mutex> g(d->_lock);
d->_wind = wind;
}
void ParticlesGlobalManager::setWindFrom(const double from_deg, const double speed_kt)
{
double map_rad = -from_deg * SG_DEGREES_TO_RADIANS;
double speed_mps = speed_kt * SG_KT_TO_MPS;
std::lock_guard<std::mutex> g(d->_lock);
d->_wind[0] = cos(map_rad) * speed_mps;
d->_wind[1] = sin(map_rad) * speed_mps;
d->_wind[2] = 0.0;
}
osg::Group* ParticlesGlobalManager::getCommonRoot()
{
std::lock_guard<std::mutex> g(d->_lock);
return d->internalGetCommonRoot();
}
osg::ref_ptr<osg::Group> ParticlesGlobalManager::appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options)
osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot,
const osgDB::Options*
options)
{
SG_LOG(SG_PARTICLES, SG_DEBUG,
"Setting up a particle system." << std::boolalpha
@@ -381,7 +152,7 @@ osg::ref_ptr<osg::Group> ParticlesGlobalManager::appendParticles(const SGPropert
<< "\n Wind: " << configNode->getChild("program")->getBoolValue("wind", true)
<< std::noboolalpha);
osg::ref_ptr<osgParticle::ParticleSystem> particleSys;
osgParticle::ParticleSystem *particleSys;
//create a generic particle system
std::string type = configNode->getStringValue("type", "normal");
@@ -389,10 +160,11 @@ osg::ref_ptr<osg::Group> ParticlesGlobalManager::appendParticles(const SGPropert
particleSys = new osgParticle::ParticleSystem;
else
particleSys = new osgParticle::ConnectedParticleSystem;
//may not be used depending on the configuration
PointerGuard<Particles> callback;
getPSU()->addParticleSystem(particleSys);
getPSU()->setUpdateCallback(new GlobalParticleCallback(modelRoot));
//contains counter, placer and shooter by default
osgParticle::ModularEmitter* emitter = new osgParticle::ModularEmitter;
@@ -400,7 +172,7 @@ osg::ref_ptr<osg::Group> ParticlesGlobalManager::appendParticles(const SGPropert
// Set up the alignment node ("stolen" from animation.cxx)
// XXX Order of rotations is probably not correct.
osg::ref_ptr<osg::MatrixTransform> align = new osg::MatrixTransform;
osg::MatrixTransform *align = new osg::MatrixTransform;
osg::Matrix res_matrix;
res_matrix.makeRotate(
configNode->getFloatValue("offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
@@ -415,8 +187,12 @@ osg::ref_ptr<osg::Group> ParticlesGlobalManager::appendParticles(const SGPropert
configNode->getFloatValue("offsets/y-m", 0.0),
configNode->getFloatValue("offsets/z-m", 0.0));
align->setMatrix(res_matrix * tmat);
align->setName("particle align");
//if (dynamic_cast<CustomModularEmitter*>(emitter)==0) SG_LOG(SG_PARTICLES, SG_ALERT, "observer error\n");
//align->addObserver(dynamic_cast<CustomModularEmitter*>(emitter));
align->addChild(emitter);
//this name can be used in the XML animation as if it was a submodel
@@ -433,6 +209,7 @@ osg::ref_ptr<osg::Group> ParticlesGlobalManager::appendParticles(const SGPropert
osg::Geode* g = new osg::Geode;
g->addDrawable(particleSys);
callback()->particleFrame->addChild(g);
getCommonRoot()->addChild(callback()->particleFrame.get());
}
std::string textureFile;
if (configNode->hasValue("texture")) {
@@ -728,38 +505,66 @@ osg::ref_ptr<osg::Group> ParticlesGlobalManager::appendParticles(const SGPropert
emitter->setUpdateCallback(callback.get());
}
// touch shared data now (and not before)
{
std::lock_guard<std::mutex> g(d->_lock);
d->_updater->addParticleSystem(particleSys);
if (attach != "local") {
d->internalGetCommonRoot()->addChild(callback()->particleFrame);
}
if (!d->_globalCallbackRegistered) {
SG_LOG(SG_PARTICLES, SG_INFO, "Registering global particles callback");
d->_globalCallbackRegistered = true;
d->_longitudeNode = modelRoot->getNode("/position/longitude-deg", true);
d->_latitudeNode = modelRoot->getNode("/position/latitude-deg", true);
d->_updater->setUpdateCallback(d.get());
}
}
return align;
}
void ParticlesGlobalManager::setSwitchNode(const SGPropertyNode* n)
void Particles::operator()(osg::Node* node, osg::NodeVisitor* nv)
{
std::lock_guard<std::mutex> g(d->_lock);
d->_enabledNode = n;
}
//SG_LOG(SG_PARTICLES, SG_ALERT, "callback!\n");
this->particleSys->setFrozen(_frozen);
void ParticlesGlobalManager::setFrozen(bool b)
{
std::lock_guard<std::mutex> g(d->_lock);
d->_frozen = b;
}
using namespace osg;
if (shooterValue)
shooter->setInitialSpeedRange(shooterValue->getValue(),
(shooterValue->getValue()
+ shooterExtraRange));
if (counterValue)
counter->setRateRange(counterValue->getValue(),
counterValue->getValue() + counterExtraRange);
else if (counterCond)
counter->setRateRange(counterStaticValue,
counterStaticValue + counterStaticExtraRange);
if (!GlobalParticleCallback::getEnabled() || (counterCond && !counterCond->test()))
counter->setRateRange(0, 0);
bool colorchange=false;
for (int i = 0; i < 8; ++i) {
if (colorComponents[i]) {
staticColorComponents[i] = colorComponents[i]->getValue();
colorchange=true;
}
}
if (colorchange)
particleSys->getDefaultParticleTemplate().setColorRange(osgParticle::rangev4( Vec4(staticColorComponents[0], staticColorComponents[1], staticColorComponents[2], staticColorComponents[3]), Vec4(staticColorComponents[4], staticColorComponents[5], staticColorComponents[6], staticColorComponents[7])));
if (startSizeValue)
startSize = startSizeValue->getValue();
if (endSizeValue)
endSize = endSizeValue->getValue();
if (startSizeValue || endSizeValue)
particleSys->getDefaultParticleTemplate().setSizeRange(osgParticle::rangef(startSize, endSize));
if (lifeValue)
particleSys->getDefaultParticleTemplate().setLifeTime(lifeValue->getValue());
if (particleFrame.valid()) {
MatrixList mlist = node->getWorldMatrices();
if (!mlist.empty()) {
const Matrix& particleMat = particleFrame->getMatrix();
Vec3d emitOrigin(mlist[0](3, 0), mlist[0](3, 1), mlist[0](3, 2));
Vec3d displace
= emitOrigin - Vec3d(particleMat(3, 0), particleMat(3, 1),
particleMat(3, 2));
if (displace * displace > 10000.0 * 10000.0) {
// Make new frame for particle system, coincident with
// the emitter frame, but oriented with local Z.
SGGeod geod = SGGeod::fromCart(toSG(emitOrigin));
Matrix newParticleMat = makeZUpFrame(geod);
Matrix changeParticleFrame
= particleMat * Matrix::inverse(newParticleMat);
particleFrame->setMatrix(newParticleMat);
transformParticles(particleSys.get(), changeParticleFrame);
}
}
}
if (program.valid() && useWind)
program->setWind(_wind);
}
} // namespace simgear

View File

@@ -1,5 +1,5 @@
// particles.hxx - classes to manage particles
// Copyright (C) 2008 Tiago Gusm<73>o
// Copyright (C) 2008 Tiago Gusm<73>o
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
@@ -53,37 +53,133 @@ class ParticleSystemUpdater;
#include <simgear/math/SGMatrix.hxx>
// Has anyone done anything *really* stupid, like making min and max macros?
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
#include "animation.hxx"
namespace simgear
{
class ParticlesGlobalManager;
class GlobalParticleCallback : public osg::NodeCallback
{
public:
GlobalParticleCallback(const SGPropertyNode* modelRoot)
{
this->modelRoot=modelRoot;
}
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
static const osg::Vec3 &getGravityVector()
{
return gravity;
}
static const osg::Vec3 &getWindVector()
{
return wind;
}
static void setSwitch(const SGPropertyNode* n)
{
enabledNode = n;
}
static bool getEnabled()
{
return enabled;
}
private:
static osg::Vec3 gravity;
static osg::Vec3 wind;
SGConstPropertyNode_ptr modelRoot;
static SGConstPropertyNode_ptr enabledNode;
static bool enabled;
};
class Particles : public osg::NodeCallback
{
public:
Particles() = default;
Particles();
void operator()(osg::Node* node, osg::NodeVisitor* nv) override;
static osg::Group * appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options);
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
void setupShooterSpeedData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot);
SGPropertyNode* modelRoot)
{
shooterValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if(!shooterValue){
SG_LOG(SG_GENERAL, SG_ALERT, "shooter property error!\n");
}
shooterExtraRange = configNode->getFloatValue("extrarange",0);
}
void setupCounterData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot);
SGPropertyNode* modelRoot)
{
counterValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if(!counterValue){
SG_LOG(SG_GENERAL, SG_ALERT, "counter property error!\n");
}
counterExtraRange = configNode->getFloatValue("extrarange",0);
}
void setupCounterCondition(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot);
SGPropertyNode* modelRoot)
{
counterCond = sgReadCondition(modelRoot, configNode);
}
void setupCounterCondition(float counterStaticValue,
float counterStaticExtraRange);
float counterStaticExtraRange)
{
this->counterStaticValue = counterStaticValue;
this->counterStaticExtraRange = counterStaticExtraRange;
}
void setupStartSizeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot);
SGPropertyNode* modelRoot)
{
startSizeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if(!startSizeValue)
{
SG_LOG(SG_GENERAL, SG_ALERT, "startSizeValue error!\n");
}
}
void setupEndSizeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot);
SGPropertyNode* modelRoot)
{
endSizeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if(!endSizeValue){
SG_LOG(SG_GENERAL, SG_ALERT, "startSizeValue error!\n");
}
}
void setupLifeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot);
SGPropertyNode* modelRoot)
{
lifeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if(!lifeValue){
SG_LOG(SG_GENERAL, SG_ALERT, "lifeValue error!\n");
}
}
void setupStaticSizeData(float startSize, float endSize)
{
@@ -116,14 +212,60 @@ public:
//component: r=0, g=1, ... ; color: 0=start color, 1=end color
void setupColorComponent(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot, int color,
int component);
int component)
{
SGExpressiond *colorValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(),
SGLimitsd::max());
if(!colorValue){
SG_LOG(SG_GENERAL, SG_ALERT, "color property error!\n");
}
colorComponents[(color*4)+component] = colorValue;
//number of color components = 4
}
void setupStaticColorComponent(float r1, float g1, float b1, float a1,
float r2, float g2, float b2, float a2);
void setupStaticColorComponent(float r1,float g1, float b1, float a1,
float r2, float g2, float b2, float a2)
{
staticColorComponents[0] = r1;
staticColorComponents[1] = g1;
staticColorComponents[2] = b1;
staticColorComponents[3] = a1;
staticColorComponents[4] = r2;
staticColorComponents[5] = g2;
staticColorComponents[6] = b2;
staticColorComponents[7] = a2;
}
static osg::Group * getCommonRoot();
static osg::Geode * getCommonGeode()
{
return commonGeode.get();
}
static osgParticle::ParticleSystemUpdater * getPSU()
{
return psu.get();
}
static void setFrozen(bool e) { _frozen = e; }
/**
* Set and get the wind vector for particles in the
* atmosphere. This vector is in the Z-up Y-north frame, and the
* magnitude is the velocity in meters per second.
*/
static void setWindVector(const osg::Vec3& wind) { _wind = wind; }
static void setWindFrom(const double from_deg, const double speed_kt) {
double map_rad = -from_deg * SG_DEGREES_TO_RADIANS;
double speed_mps = speed_kt * SG_KT_TO_MPS;
_wind[0] = cos(map_rad) * speed_mps;
_wind[1] = sin(map_rad) * speed_mps;
_wind[2] = 0.0;
}
static const osg::Vec3& getWindVector() { return _wind; }
protected:
friend class ParticlesGlobalManager;
float shooterExtraRange;
float counterExtraRange;
SGSharedPtr<SGExpressiond> shooterValue;
@@ -143,54 +285,39 @@ protected:
osg::ref_ptr<osgParticle::ParticleSystem> particleSys;
osg::ref_ptr<osgParticle::FluidProgram> program;
osg::ref_ptr<osg::MatrixTransform> particleFrame;
bool useGravity = false;
bool useWind = false;
bool useGravity;
bool useWind;
static bool _frozen;
static osg::ref_ptr<osgParticle::ParticleSystemUpdater> psu;
static osg::ref_ptr<osg::Group> commonRoot;
static osg::ref_ptr<osg::Geode> commonGeode;
static osg::Vec3 _wind;
};
}
#endif
class ParticlesGlobalManager
/*
class CustomModularEmitter : public osgParticle::ModularEmitter, public osg::Observer
{
public:
~ParticlesGlobalManager();
static ParticlesGlobalManager* instance();
static void clear();
virtual void objectDeleted (void *)
{
SG_LOG(SG_GENERAL, SG_ALERT, "object deleted!\n");
bool isEnabled() const;
SGParticles::getCommonRoot()->removeChild(this->getParticleSystem()->getParent(0));
SGParticles::getPSU()->removeParticleSystem(this->getParticleSystem());
}
osg::ref_ptr<osg::Group> appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options);
osg::Group* getCommonRoot();
osg::Geode* getCommonGeode();
osgParticle::ParticleSystemUpdater* getPSU();
void setFrozen(bool e);
bool isFrozen() const;
void setSwitchNode(const SGPropertyNode* n);
/**
* Set and get the wind vector for particles in the
* atmosphere. This vector is in the Z-up Y-north frame, and the
* magnitude is the velocity in meters per second.
*/
void setWindVector(const osg::Vec3& wind);
void setWindFrom(const double from_deg, const double speed_kt);
osg::Vec3 getWindVector() const;
private:
ParticlesGlobalManager();
class ParticlesGlobalManagerPrivate;
// because Private inherits NodeCallback, we need to own it
// via an osg::ref_ptr
osg::ref_ptr<ParticlesGlobalManagerPrivate> d;
// ~CustomModularEmitter()
// {
// SG_LOG(SG_GENERAL, SG_ALERT, "object deleted!\n");
//
// SGParticles::getCommonRoot()->removeChild(this->getParticleSystem()->getParent(0));
// SGParticles::getPSU()->removeParticleSystem(this->getParticleSystem());
// }
};
} // namespace simgear
#endif
*/

View File

@@ -221,41 +221,29 @@ struct ReaderWriterSTG::_ModelBin {
if (signBuilder.getSignsGroup())
group->addChild(signBuilder.getSignsGroup());
if (!_buildingList.empty()) {
if (_buildingList.size() > 0) {
SGMaterialLibPtr matlib = _options->getMaterialLib();
bool useVBOs = (_options->getPluginStringData("SimGear::USE_VBOS") == "ON");
if (!matlib) {
SG_LOG( SG_TERRAIN, SG_ALERT, "Unable to get materials definition for buildings");
} else {
for (const auto& b : _buildingList) {
for (std::list<_BuildingList>::iterator i = _buildingList.begin(); i != _buildingList.end(); ++i) {
// Build buildings for each list of buildings
SGGeod geodPos = SGGeod::fromDegM(b._lon, b._lat, 0.0);
SGSharedPtr<SGMaterial> mat = matlib->find(b._material_name, geodPos);
// trying to avoid crash on null material, see:
// https://sentry.io/organizations/flightgear/issues/1867075869
if (!mat) {
SG_LOG(SG_TERRAIN, SG_ALERT, "Building specifies unknown material: " << b._material_name);
continue;
}
const auto path = SGPath(b._filename);
SGGeod geodPos = SGGeod::fromDegM(i->_lon, i->_lat, 0.0);
SGMaterial* mat = matlib->find(i->_material_name, geodPos);
SGPath path = SGPath(i->_filename);
SGBuildingBin* buildingBin = new SGBuildingBin(path, mat, useVBOs);
SGBuildingBinList bbList;
bbList.push_back(buildingBin);
SGBuildingBinList buildingBinList;
buildingBinList.push_back(buildingBin);
osg::MatrixTransform* matrixTransform;
matrixTransform = new osg::MatrixTransform(makeZUpFrame(SGGeod::fromDegM(b._lon, b._lat, b._elev)));
matrixTransform = new osg::MatrixTransform(makeZUpFrame(SGGeod::fromDegM(i->_lon, i->_lat, i->_elev)));
matrixTransform->setName("rotateBuildings");
matrixTransform->setDataVariance(osg::Object::STATIC);
matrixTransform->addChild(createRandomBuildings(bbList, osg::Matrix::identity(), _options));
matrixTransform->addChild(createRandomBuildings(buildingBinList, osg::Matrix::identity(), _options));
group->addChild(matrixTransform);
std::for_each(bbList.begin(), bbList.end(), [](SGBuildingBin* bb) {
delete bb;
});
}
}
}

View File

@@ -462,17 +462,9 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
// F is the number of floors (integer)
// WT is the texture index to use (integer) for walls. Buildings with the same WT value will have the same wall texture assigned. There are 6 small, 6 medium and 4 large textures.
// RT is the texture index to use (integer) for roofs. Buildings with the same RT value will have the same roof texture assigned. There are 6 small, 6 medium and 4 large textures.
float x = 0.0f, y = 0.0f, z = 0.0f, r = 0.0f, w = 0.0f, d = 0.0f, h = 0.0f, p = 0.0f;
int b = 0, s = 0, o = 0, f = 0, wt = 0, rt = 0;
in >> x >> y >> z >> r >> b;
if (in.bad() || in.fail()) {
SG_LOG(SG_TERRAIN, SG_WARN, "Error parsing build entry in: " << absoluteFileName << " line: \"" << line << "\"");
continue;
}
// these might fail, so check them after we look at failbit
in >> w >> d >> h >> p >> s >> o >> f >> wt >> rt;
float x, y, z, r, w, d, h, p;
int b, s, o, f, wt, rt;
in >> x >> y >> z >> r >> b >> w >> d >> h >> p >> s >> o >> f >> wt >> rt;
//SG_LOG(SG_TERRAIN, SG_ALERT, "Building entry " << x << " " << y << " " << z << " " << b );
SGVec3f loc = SGVec3f(x,y,z);
@@ -497,20 +489,14 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
};
// Set up the building set based on the material definitions
SGBuildingBin::SGBuildingBin(const SGMaterial* mat, bool useVBOs) : _material(const_cast<SGMaterial*>(mat))
{
const auto& materialNames = mat->get_names();
if (materialNames.empty()) {
SG_LOG(SG_TERRAIN, SG_DEV_ALERT, "SGBuildingBin: material has zero names defined");
} else {
_materialName = materialNames.front();
SG_LOG(SG_TERRAIN, SG_DEBUG, "Building material " << _materialName);
}
_textureName = mat->get_building_texture();
_lightMapName = mat->get_building_lightmap();
buildingRange = mat->get_building_range();
SG_LOG(SG_TERRAIN, SG_DEBUG, "Building texture " << _textureName);
SGBuildingBin::SGBuildingBin(const SGMaterial *mat, bool useVBOs) {
material_name = new std::string(mat->get_names()[0]);
SG_LOG(SG_TERRAIN, SG_DEBUG, "Building material " << material_name);
material = mat;
texture = new std::string(mat->get_building_texture());
lightMap = new std::string(mat->get_building_lightmap());
buildingRange = mat->get_building_range();
SG_LOG(SG_TERRAIN, SG_DEBUG, "Building texture " << texture);
}
SGBuildingBin::~SGBuildingBin() {
@@ -658,15 +644,15 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
if (buildingtype == SGBuildingBin::SMALL) {
// Small building
// Maximum number of floors is 3, and maximum width/depth is 192m.
width = _material->get_building_small_min_width() + mt_rand(&seed) * mt_rand(&seed) * (_material->get_building_small_max_width() - _material->get_building_small_min_width());
depth = _material->get_building_small_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (_material->get_building_small_max_depth() - _material->get_building_small_min_depth());
floors = SGMisc<double>::round(_material->get_building_small_min_floors() + mt_rand(&seed) * (_material->get_building_small_max_floors() - _material->get_building_small_min_floors()));
width = material->get_building_small_min_width() + mt_rand(&seed) * mt_rand(&seed) * (material->get_building_small_max_width() - material->get_building_small_min_width());
depth = material->get_building_small_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (material->get_building_small_max_depth() - material->get_building_small_min_depth());
floors = SGMisc<double>::round(material->get_building_small_min_floors() + mt_rand(&seed) * (material->get_building_small_max_floors() - material->get_building_small_min_floors()));
height = floors * (2.8 + mt_rand(&seed));
// Small buildings are never deeper than they are wide.
if (depth > width) { depth = width; }
pitch_height = (mt_rand(&seed) < _material->get_building_small_pitch()) ? 3.0 : 0.0;
pitch_height = (mt_rand(&seed) < material->get_building_small_pitch()) ? 3.0 : 0.0;
if (pitch_height == 0.0) {
roof_shape = 0;
@@ -677,18 +663,18 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
}
} else if (buildingtype == SGBuildingBin::MEDIUM) {
// MEDIUM BUILDING
width = _material->get_building_medium_min_width() + mt_rand(&seed) * mt_rand(&seed) * (_material->get_building_medium_max_width() - _material->get_building_medium_min_width());
depth = _material->get_building_medium_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (_material->get_building_medium_max_depth() - _material->get_building_medium_min_depth());
floors = SGMisc<double>::round(_material->get_building_medium_min_floors() + mt_rand(&seed) * (_material->get_building_medium_max_floors() - _material->get_building_medium_min_floors()));
width = material->get_building_medium_min_width() + mt_rand(&seed) * mt_rand(&seed) * (material->get_building_medium_max_width() - material->get_building_medium_min_width());
depth = material->get_building_medium_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (material->get_building_medium_max_depth() - material->get_building_medium_min_depth());
floors = SGMisc<double>::round(material->get_building_medium_min_floors() + mt_rand(&seed) * (material->get_building_medium_max_floors() - material->get_building_medium_min_floors()));
height = floors * (2.8 + mt_rand(&seed));
while ((height > width) && (floors > _material->get_building_medium_min_floors())) {
// Ensure that medium buildings aren't taller than they are wide
floors--;
height = floors * (2.8 + mt_rand(&seed));
while ((height > width) && (floors > material->get_building_medium_min_floors())) {
// Ensure that medium buildings aren't taller than they are wide
floors--;
height = floors * (2.8 + mt_rand(&seed));
}
pitch_height = (mt_rand(&seed) < _material->get_building_medium_pitch()) ? 3.0 : 0.0;
pitch_height = (mt_rand(&seed) < material->get_building_medium_pitch()) ? 3.0 : 0.0;
if (pitch_height == 0.0) {
roof_shape = 0;
@@ -699,11 +685,11 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
}
} else {
// LARGE BUILDING
width = _material->get_building_large_min_width() + mt_rand(&seed) * (_material->get_building_large_max_width() - _material->get_building_large_min_width());
depth = _material->get_building_large_min_depth() + mt_rand(&seed) * (_material->get_building_large_max_depth() - _material->get_building_large_min_depth());
floors = SGMisc<double>::round(_material->get_building_large_min_floors() + mt_rand(&seed) * (_material->get_building_large_max_floors() - _material->get_building_large_min_floors()));
width = material->get_building_large_min_width() + mt_rand(&seed) * (material->get_building_large_max_width() - material->get_building_large_min_width());
depth = material->get_building_large_min_depth() + mt_rand(&seed) * (material->get_building_large_max_depth() - material->get_building_large_min_depth());
floors = SGMisc<double>::round(material->get_building_large_min_floors() + mt_rand(&seed) * (material->get_building_large_max_floors() - material->get_building_large_min_floors()));
height = floors * (2.8 + mt_rand(&seed));
pitch_height = (mt_rand(&seed) < _material->get_building_large_pitch()) ? 3.0 : 0.0;
pitch_height = (mt_rand(&seed) < material->get_building_large_pitch()) ? 3.0 : 0.0;
if (pitch_height == 0.0) {
roof_shape = 0;
@@ -732,35 +718,36 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
}
SGBuildingBin::BuildingType SGBuildingBin::getBuildingType(float roll) {
if (roll < _material->get_building_small_fraction()) {
return SGBuildingBin::SMALL;
}
if (roll < (_material->get_building_small_fraction() + _material->get_building_medium_fraction())) {
return SGBuildingBin::MEDIUM;
}
if (roll < material->get_building_small_fraction()) {
return SGBuildingBin::SMALL;
}
if (roll < (material->get_building_small_fraction() + material->get_building_medium_fraction())) {
return SGBuildingBin::MEDIUM;
}
return SGBuildingBin::LARGE;
}
float SGBuildingBin::getBuildingMaxRadius(BuildingType type) {
if (type == SGBuildingBin::SMALL) return _material->get_building_small_max_width();
if (type == SGBuildingBin::MEDIUM) return _material->get_building_medium_max_width();
if (type == SGBuildingBin::LARGE) return _material->get_building_large_max_width();
return 0;
if (type == SGBuildingBin::SMALL) return material->get_building_small_max_width();
if (type == SGBuildingBin::MEDIUM) return material->get_building_medium_max_width();
if (type == SGBuildingBin::LARGE) return material->get_building_large_max_width();
return 0;
}
float SGBuildingBin::getBuildingMaxDepth(BuildingType type) {
if (type == SGBuildingBin::SMALL) return _material->get_building_small_max_depth();
if (type == SGBuildingBin::MEDIUM) return _material->get_building_medium_max_depth();
if (type == SGBuildingBin::LARGE) return _material->get_building_large_max_depth();
return 0;
if (type == SGBuildingBin::SMALL) return material->get_building_small_max_depth();
if (type == SGBuildingBin::MEDIUM) return material->get_building_medium_max_depth();
if (type == SGBuildingBin::LARGE) return material->get_building_large_max_depth();
return 0;
}
ref_ptr<Group> SGBuildingBin::createBuildingsGroup(Matrix transInv, const SGReaderWriterOptions* options)
{
ref_ptr<Effect> effect;
auto iter = buildingEffectMap.find(_textureName);
EffectMap::iterator iter = buildingEffectMap.find(*texture);
if ((iter == buildingEffectMap.end())||
(!iter->second.lock(effect)))
@@ -769,14 +756,16 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
makeChild(effectProp, "inherits-from")->setStringValue("Effects/building");
SGPropertyNode* params = makeChild(effectProp, "parameters");
// Main texture - n=0
params->getChild("texture", 0, true)->getChild("image", 0, true)->setStringValue(_textureName);
params->getChild("texture", 0, true)->getChild("image", 0, true)
->setStringValue(*texture);
// Light map - n=3
params->getChild("texture", 3, true)->getChild("image", 0, true)->setStringValue(_lightMapName);
params->getChild("texture", 3, true)->getChild("image", 0, true)
->setStringValue(*lightMap);
effect = makeEffect(effectProp, true, options);
if (iter == buildingEffectMap.end())
buildingEffectMap.insert(EffectMap::value_type(_textureName, effect));
buildingEffectMap.insert(EffectMap::value_type(*texture, effect));
else
iter->second = effect; // update existing, but empty observer
}

View File

@@ -145,19 +145,20 @@ public:
};
private:
const SGSharedPtr<SGMaterial> _material;
std::string _materialName;
std::string _textureName;
std::string _lightMapName;
const SGMaterial *material;
// Visibility range for buildings
float buildingRange;
std::string* material_name;
std::string* texture;
std::string* lightMap;
// Visibility range for buildings
float buildingRange;
// Information for an instance of a building - position and orientation
typedef std::vector<BuildingInstance> BuildingInstanceList;
BuildingInstanceList buildingLocations;
// Information for an instance of a building - position and orientation
typedef std::vector<BuildingInstance> BuildingInstanceList;
BuildingInstanceList buildingLocations;
public:
@@ -186,7 +187,7 @@ public:
bool checkMinDist (SGVec3f p, float radius);
const std::string& getMaterialName() const { return _materialName; }
std::string* getMaterialName() { return material_name; }
BuildingType getBuildingType(float roll);
float getBuildingMaxRadius(BuildingType);

View File

@@ -428,12 +428,12 @@ public:
return 0;
// FIXME: do not include all values here ...
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array;
osg::ref_ptr<osg::Vec2Array> priTexCoords = new osg::Vec2Array;
osg::ref_ptr<osg::Vec2Array> secTexCoords = new osg::Vec2Array;
osg::Vec3Array* vertices = new osg::Vec3Array;
osg::Vec3Array* normals = new osg::Vec3Array;
osg::Vec2Array* priTexCoords = new osg::Vec2Array;
osg::Vec2Array* secTexCoords = new osg::Vec2Array;
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array;
osg::Vec4Array* colors = new osg::Vec4Array;
colors->push_back(osg::Vec4(1, 1, 1, 1));
osg::Geometry* geometry = new osg::Geometry;

View File

@@ -151,26 +151,14 @@ Geometry* makeSharedTreeGeometry(int numQuads)
return result;
}
static std::mutex static_sharedGeometryMutex;
static ref_ptr<Geometry> sharedTreeGeometry;
void clearSharedTreeGeometry()
{
std::lock_guard<std::mutex> g(static_sharedGeometryMutex);
sharedTreeGeometry = {};
}
ref_ptr<Geometry> sharedTreeGeometry;
Geometry* createTreeGeometry(float width, float height, int varieties)
{
Geometry* quadGeom = nullptr;
{
std::lock_guard<std::mutex> g(static_sharedGeometryMutex);
if (!sharedTreeGeometry)
sharedTreeGeometry = makeSharedTreeGeometry(1600);
quadGeom = simgear::clone(sharedTreeGeometry.get(),
CopyOp::SHALLOW_COPY);
}
if (!sharedTreeGeometry)
sharedTreeGeometry = makeSharedTreeGeometry(1600);
Geometry* quadGeom = simgear::clone(sharedTreeGeometry.get(),
CopyOp::SHALLOW_COPY);
Vec3Array* params = new Vec3Array;
params->push_back(Vec3(width, height, (float)varieties));
quadGeom->setNormalArray(params);

View File

@@ -35,14 +35,11 @@ namespace simgear
{
class TreeBin {
public:
~TreeBin() = default;
struct Tree {
SGVec3f position;
SGVec3f tnormal;
Tree(const SGVec3f& p, const SGVec3f& t) : position(p), tnormal(t)
{
}
struct Tree {
SGVec3f position;
SGVec3f tnormal;
Tree(const SGVec3f& p, const SGVec3f& t) : position(p),tnormal(t)
{ }
};
typedef std::vector<Tree> TreeList;
@@ -58,25 +55,19 @@ public:
{ _trees.push_back(t); }
void insert(const SGVec3f& p, const SGVec3f& tnorm)
{
_trees.emplace_back(p, tnorm);
}
{insert(Tree(p,tnorm));}
unsigned getNumTrees() const
{ return _trees.size(); }
const Tree& getTree(unsigned i) const
{
assert(i < _trees.size());
return _trees.at(i);
}
{ return _trees[i]; }
TreeList _trees;
~TreeBin() {
_trees.clear();
}
};
void clearSharedTreeGeometry();
typedef std::list<TreeBin*> SGTreeBinList;
osg::Group* createForest(SGTreeBinList& forestList, const osg::Matrix& transform,

View File

@@ -280,18 +280,9 @@ SGLightFactory::getLights(const SGDirectionalLightBin& lights)
//stateSet->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin");
//stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
static SGSceneFeatures* sceneFeatures = SGSceneFeatures::instance();
bool useTriangles = sceneFeatures->getEnableTriangleDirectionalLights();
osg::DrawArrays* drawArrays;
if (useTriangles)
drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,
0, vertices->size());
else
drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS,
0, vertices->size());
drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,
0, vertices->size());
geometry->addPrimitiveSet(drawArrays);
return geometry;
}

View File

@@ -46,7 +46,6 @@
#include <fstream>
#include <string>
#include <map>
#include <algorithm>
#include <simgear/version.h>
@@ -156,16 +155,20 @@ public:
class SyncSlot
{
public:
SyncSlot() = default;
SyncSlot() :
isNewDirectory(false),
busy(false),
pendingKBytes(0)
{}
SyncItem currentItem;
bool isNewDirectory = false;
std::deque<SyncItem> queue;
bool isNewDirectory;
std::queue<SyncItem> queue;
std::unique_ptr<HTTPRepository> repository;
SGTimeStamp stamp;
bool busy = false; ///< is the slot working or idle
unsigned int pendingKBytes = 0;
unsigned int nextWarnTimeout = 0;
bool busy; ///< is the slot working or idle
unsigned int pendingKBytes;
unsigned int nextWarnTimeout;
};
static const int SYNC_SLOT_TILES = 0; ///< Terrain and Objects sync
@@ -202,6 +205,7 @@ struct TerrasyncThreadState
_updated_tile_count(0),
_success_count(0),
_consecutive_errors(0),
_allowed_errors(6),
_cache_hits(0),
_transfer_rate(0),
_total_kb_downloaded(0),
@@ -215,6 +219,7 @@ struct TerrasyncThreadState
int _updated_tile_count;
int _success_count;
int _consecutive_errors;
int _allowed_errors;
int _cache_hits;
int _transfer_rate;
// kbytes, not bytes, because bytes might overflow 2^31
@@ -301,6 +306,13 @@ public:
void setInstalledDir(const SGPath& p) { _installRoot = p; }
void setAllowedErrorCount(int errors)
{
std::lock_guard<std::mutex> g(_stateLock);
_state._allowed_errors = errors;
}
void setCachePath(const SGPath& p) {_persistentCachePath = p;}
void setCacheHits(unsigned int hits)
{
std::lock_guard<std::mutex> g(_stateLock);
@@ -316,12 +328,7 @@ public:
}
return st;
}
bool isDirActive(const std::string& path) const;
void setCachePath(const SGPath &p) { _persistentCachePath = p; }
private:
private:
void incrementCacheHits()
{
std::lock_guard<std::mutex> g(_stateLock);
@@ -334,18 +341,15 @@ public:
void runInternal();
void updateSyncSlot(SyncSlot& slot);
void drainWaitingTiles();
// commond helpers between both internal and external models
SyncItem::Status isPathCached(const SyncItem& next) const;
void initCompletedTilesPersistentCache();
void writeCompletedTilesPersistentCache() const;
void updated(SyncItem item, bool isNewDirectory);
void fail(SyncItem failedItem);
void notFound(SyncItem notFoundItem);
void initCompletedTilesPersistentCache();
void writeCompletedTilesPersistentCache() const;
HTTP::Client _http;
SyncSlot _syncSlots[NUM_SYNC_SLOTS];
@@ -366,7 +370,7 @@ public:
string _dnsdn;
TerrasyncThreadState _state;
mutable std::mutex _stateLock;
std::mutex _stateLock;
};
SGTerraSync::WorkerThread::WorkerThread() :
@@ -394,18 +398,6 @@ void SGTerraSync::WorkerThread::stop()
SyncItem w(string(), SyncItem::Stop);
request(w);
join();
// clear the sync slots, in case we restart
for (unsigned int slot = 0; slot < NUM_SYNC_SLOTS; ++slot) {
_syncSlots[slot] = {};
}
// clear these so if re-init-ing, we check again
_completedTiles.clear();
_notFoundItems.clear();
_http.reset();
_http.setUserAgent("terrascenery-" SG_STRINGIZE(SIMGEAR_VERSION));
}
bool SGTerraSync::WorkerThread::start()
@@ -425,18 +417,12 @@ bool SGTerraSync::WorkerThread::start()
SGPath path(_local_dir);
if (!path.exists())
{
const SGPath parentDir = path.dirPath();
if (parentDir.exists()) {
// attempt to create terraSync dir ourselves
bool ok = path.create_dir(0755);
if (!ok) {
SG_LOG(SG_TERRASYNC, SG_ALERT,
"Cannot start scenery download. Directory '" << _local_dir << "' does not exist. Set correct directory path or create directory folder.");
_state._fail_count++;
_state._stalled = true;
return false;
}
}
SG_LOG(SG_TERRASYNC,SG_ALERT,
"Cannot start scenery download. Directory '" << _local_dir <<
"' does not exist. Set correct directory path or create directory folder.");
_state._fail_count++;
_state._stalled = true;
return false;
}
path.append("version");
@@ -453,8 +439,10 @@ bool SGTerraSync::WorkerThread::start()
_stop = false;
_state = TerrasyncThreadState(); // clean state
SG_LOG(SG_TERRASYNC, SG_MANDATORY_INFO,
"Starting automatic scenery download/synchronization to '" << _local_dir << "'.");
// not really an alert - but we want to (always) see this message, so user is
// aware we're downloading scenery (and using bandwidth).
SG_LOG(SG_TERRASYNC,SG_ALERT,
"Starting automatic scenery download/synchronization to '"<< _local_dir << "'.");
SGThread::start();
return true;
@@ -536,6 +524,7 @@ void SGTerraSync::WorkerThread::run()
}
initCompletedTilesPersistentCache();
runInternal();
{
@@ -552,7 +541,7 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
if (slot.stamp.elapsedMSec() > (int)slot.nextWarnTimeout) {
SG_LOG(SG_TERRASYNC, SG_INFO, "sync taking a long time:" << slot.currentItem._dir << " taken " << slot.stamp.elapsedMSec());
SG_LOG(SG_TERRASYNC, SG_INFO, "HTTP request count:" << _http.hasActiveRequests());
slot.nextWarnTimeout += 30 * 1000;
slot.nextWarnTimeout += 10000;
}
#endif
// convert bytes to kbytes here
@@ -562,7 +551,6 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
// check result
HTTPRepository::ResultCode res = slot.repository->failure();
if (res == HTTPRepository::REPO_ERROR_NOT_FOUND) {
notFound(slot.currentItem);
} else if (res != HTTPRepository::REPO_NO_ERROR) {
@@ -577,13 +565,12 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
slot.busy = false;
slot.repository.reset();
slot.pendingKBytes = 0;
slot.currentItem = {};
}
// init and start sync of the next repository
if (!slot.queue.empty()) {
slot.currentItem = slot.queue.front();
slot.queue.pop_front();
slot.queue.pop();
SGPath path(_local_dir);
path.append(slot.currentItem._dir);
@@ -598,32 +585,8 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
}
} // of creating directory step
// optimise initial Airport download
if (slot.isNewDirectory &&
(slot.currentItem._type == SyncItem::AirportData)) {
SG_LOG(SG_TERRASYNC, SG_INFO, "doing Airports download via tarball");
// we want to sync the 'root' TerraSync dir, but not all of it, just
// the Airports_archive.tar.gz file so we use our TerraSync local root
// as the path (since the archive will add Airports/)
slot.repository.reset(new HTTPRepository(_local_dir, &_http));
slot.repository->setBaseUrl(_httpServer + "/");
// filter callback to *only* sync the Airport_archive tarball,
// and ensure no other contents are touched
auto f = [](const HTTPRepository::SyncItem &item) {
if (!item.directory.empty())
return false;
return (item.filename.find("Airports_archive.") == 0);
};
slot.repository->setFilter(f);
} else {
slot.repository.reset(new HTTPRepository(path, &_http));
slot.repository->setBaseUrl(_httpServer + "/" +
slot.currentItem._dir);
}
slot.repository.reset(new HTTPRepository(path, &_http));
slot.repository->setBaseUrl(_httpServer + "/" + slot.currentItem._dir);
if (_installRoot.exists()) {
SGPath p = _installRoot;
@@ -642,7 +605,7 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
return;
}
slot.nextWarnTimeout = 30 * 1000;
slot.nextWarnTimeout = 20000;
slot.stamp.stamp();
slot.busy = true;
slot.pendingKBytes = slot.repository->bytesToDownload();
@@ -653,23 +616,21 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
void SGTerraSync::WorkerThread::runInternal()
{
unsigned dnsRetryCount = 0;
while (!_stop) {
// try to find a terrasync server
if( !hasServer() ) {
const auto haveServer = findServer();
if (haveServer) {
hasServer(true);
std::lock_guard<std::mutex> g(_stateLock);
_state._consecutive_errors = 0;
SG_LOG(SG_TERRASYNC, SG_INFO, "terrasync scenery provider of the day is '" << _httpServer << "'");
} else {
std::lock_guard<std::mutex> g(_stateLock);
_state._consecutive_errors++;
}
continue;
if( ++dnsRetryCount > 5 ) {
SG_LOG(SG_TERRASYNC, SG_WARN, "Can't find a terrasync server. TS disabled.");
break;
}
if( hasServer( findServer() ) ) {
SG_LOG(SG_TERRASYNC, SG_INFO, "terrasync scenery provider of the day is '" << _httpServer << "'");
}
continue;
}
dnsRetryCount = 0;
try {
_http.update(10);
@@ -687,7 +648,21 @@ void SGTerraSync::WorkerThread::runInternal()
if (_stop)
break;
drainWaitingTiles();
// drain the waiting tiles queue into the sync slot queues.
while (!waitingTiles.empty()) {
SyncItem next = waitingTiles.pop_front();
SyncItem::Status cacheStatus = isPathCached(next);
if (cacheStatus != SyncItem::Invalid) {
incrementCacheHits();
SG_LOG(SG_TERRASYNC, SG_DEBUG, "\nTerraSync Cache hit for: '" << next._dir << "'");
next._status = cacheStatus;
_freshTiles.push_back(next);
continue;
}
unsigned int slot = syncSlotForType(next._type);
_syncSlots[slot].queue.push(next);
}
bool anySlotBusy = false;
unsigned int newPendingCount = 0;
@@ -716,7 +691,7 @@ void SGTerraSync::WorkerThread::runInternal()
SyncItem::Status SGTerraSync::WorkerThread::isPathCached(const SyncItem& next) const
{
auto ii = _completedTiles.find(next._dir);
TileAgeCache::const_iterator ii = _completedTiles.find( next._dir );
if (ii == _completedTiles.end()) {
ii = _notFoundItems.find( next._dir );
// Invalid means 'not cached', otherwise we want to return to
@@ -744,7 +719,6 @@ void SGTerraSync::WorkerThread::fail(SyncItem failedItem)
_state._fail_count++;
failedItem._status = SyncItem::Failed;
_freshTiles.push_back(failedItem);
// not we also end up here for partial syncs
SG_LOG(SG_TERRASYNC,SG_INFO,
"Failed to sync'" << failedItem._dir << "'");
_completedTiles[ failedItem._dir ] = now + UpdateInterval::FailedAttempt;
@@ -786,117 +760,68 @@ void SGTerraSync::WorkerThread::updated(SyncItem item, bool isNewDirectory)
writeCompletedTilesPersistentCache();
}
void SGTerraSync::WorkerThread::drainWaitingTiles()
void SGTerraSync::WorkerThread::initCompletedTilesPersistentCache()
{
// drain the waiting tiles queue into the sync slot queues.
while (!waitingTiles.empty()) {
SyncItem next = waitingTiles.pop_front();
SyncItem::Status cacheStatus = isPathCached(next);
if (cacheStatus != SyncItem::Invalid) {
incrementCacheHits();
SG_LOG(SG_TERRASYNC, SG_BULK, "\nTerraSync Cache hit for: '" << next._dir << "'");
next._status = cacheStatus;
_freshTiles.push_back(next);
if (!_persistentCachePath.exists()) {
return;
}
SGPropertyNode_ptr cacheRoot(new SGPropertyNode);
time_t now = time(0);
try {
readProperties(_persistentCachePath, cacheRoot);
} catch (sg_exception& e) {
SG_LOG(SG_TERRASYNC, SG_INFO, "corrupted persistent cache, discarding " << e.getFormattedMessage());
return;
}
for (int i=0; i<cacheRoot->nChildren(); ++i) {
SGPropertyNode* entry = cacheRoot->getChild(i);
bool isNotFound = (strcmp(entry->getName(), "not-found") == 0);
string tileName = entry->getStringValue("path");
time_t stamp = entry->getIntValue("stamp");
if (stamp < now) {
continue;
}
const auto slot = syncSlotForType(next._type);
_syncSlots[slot].queue.push_back(next);
}
}
bool SGTerraSync::WorkerThread::isDirActive(const std::string& path) const
{
// check waiting tiles first. we have to copy it to check safely,
// but since it's normally empty, this is not a big deal.
const auto copyOfWaiting = waitingTiles.copy();
auto it = std::find_if(copyOfWaiting.begin(), copyOfWaiting.end(), [&path](const SyncItem& i) {
return i._dir == path;
});
if (it != copyOfWaiting.end()) {
return true;
}
// check each sync slot in turn
std::lock_guard<std::mutex> g(_stateLock);
for (unsigned int slot = 0; slot < NUM_SYNC_SLOTS; ++slot) {
const auto& syncSlot = _syncSlots[slot];
if (syncSlot.currentItem._dir == path)
return true;
auto it = std::find_if(syncSlot.queue.begin(), syncSlot.queue.end(), [&path](const SyncItem& i) {
return i._dir == path;
});
if (it != syncSlot.queue.end()) {
return true;
if (isNotFound) {
_completedTiles[tileName] = stamp;
} else {
_notFoundItems[tileName] = stamp;
}
} // of sync slots iteration
return false;
}
}
void SGTerraSync::WorkerThread::initCompletedTilesPersistentCache() {
if (!_persistentCachePath.exists()) {
return;
}
SGPropertyNode_ptr cacheRoot(new SGPropertyNode);
time_t now = time(0);
try {
readProperties(_persistentCachePath, cacheRoot);
} catch (sg_exception &e) {
SG_LOG(SG_TERRASYNC, SG_INFO, "corrupted persistent cache, discarding");
return;
}
for (int i = 0; i < cacheRoot->nChildren(); ++i) {
SGPropertyNode *entry = cacheRoot->getChild(i);
bool isNotFound = (strcmp(entry->getName(), "not-found") == 0);
string tileName = entry->getStringValue("path");
time_t stamp = entry->getIntValue("stamp");
if (stamp < now) {
continue;
void SGTerraSync::WorkerThread::writeCompletedTilesPersistentCache() const
{
// cache is disabled
if (_persistentCachePath.isNull()) {
return;
}
if (isNotFound) {
_notFoundItems[tileName] = stamp;
} else {
_completedTiles[tileName] = stamp;
sg_ofstream f(_persistentCachePath, std::ios::trunc);
if (!f.is_open()) {
return;
}
}
}
void SGTerraSync::WorkerThread::writeCompletedTilesPersistentCache() const {
// cache is disabled
if (_persistentCachePath.isNull()) {
return;
}
SGPropertyNode_ptr cacheRoot(new SGPropertyNode);
TileAgeCache::const_iterator it = _completedTiles.begin();
for (; it != _completedTiles.end(); ++it) {
SGPropertyNode* entry = cacheRoot->addChild("entry");
entry->setStringValue("path", it->first);
entry->setIntValue("stamp", it->second);
}
sg_ofstream f(_persistentCachePath, std::ios::trunc);
if (!f.is_open()) {
return;
}
it = _notFoundItems.begin();
for (; it != _notFoundItems.end(); ++it) {
SGPropertyNode* entry = cacheRoot->addChild("not-found");
entry->setStringValue("path", it->first);
entry->setIntValue("stamp", it->second);
}
SGPropertyNode_ptr cacheRoot(new SGPropertyNode);
TileAgeCache::const_iterator it = _completedTiles.begin();
for (; it != _completedTiles.end(); ++it) {
SGPropertyNode *entry = cacheRoot->addChild("entry");
entry->setStringValue("path", it->first);
entry->setIntValue("stamp", it->second);
}
it = _notFoundItems.begin();
for (; it != _notFoundItems.end(); ++it) {
SGPropertyNode *entry = cacheRoot->addChild("not-found");
entry->setStringValue("path", it->first);
entry->setIntValue("stamp", it->second);
}
writeProperties(f, cacheRoot, true /* write_all */);
f.close();
writeProperties(f, cacheRoot, true /* write_all */);
f.close();
}
///////////////////////////////////////////////////////////////////////////////
@@ -925,12 +850,6 @@ SGTerraSync::~SGTerraSync()
void SGTerraSync::setRoot(SGPropertyNode_ptr root)
{
if (!root) {
_terraRoot.clear();
_renderingRoot.clear();
return;
}
_terraRoot = root->getNode("/sim/terrasync",true);
_renderingRoot = root->getNode("/sim/rendering", true);
}
@@ -978,17 +897,11 @@ void SGTerraSync::reinit()
#else
_workerThread->setDNSDN( _terraRoot->getStringValue("dnsdn","terrasync.flightgear.org") );
#endif
SGPath sceneryRoot{_terraRoot->getStringValue("scenery-dir", "")};
_workerThread->setLocalDir(sceneryRoot.utf8Str());
_workerThread->setLocalDir(_terraRoot->getStringValue("scenery-dir",""));
SGPath installPath(_terraRoot->getStringValue("installation-dir"));
_workerThread->setInstalledDir(installPath);
if (_terraRoot->getBoolValue("enable-persistent-cache", true)) {
_workerThread->setCachePath(sceneryRoot / "RecheckCache");
}
_workerThread->setAllowedErrorCount(_terraRoot->getIntValue("max-errors",5));
_workerThread->setCacheHits(_terraRoot->getIntValue("cache-hit", 0));
if (_workerThread->start())
@@ -1031,7 +944,12 @@ void SGTerraSync::bind()
_downloadedKBtesNode = _terraRoot->getNode("downloaded-kbytes", true);
_enabledNode = _terraRoot->getNode("enabled", true);
_availableNode = _terraRoot->getNode("available", true);
_maxErrorsNode = _terraRoot->getNode("max-errors", true);
//_busyNode->setAttribute(SGPropertyNode::WRITE, false);
//_activeNode->setAttribute(SGPropertyNode::WRITE, false);
//_updateCountNode->setAttribute(SGPropertyNode::WRITE, false);
//_errorCountNode->setAttribute(SGPropertyNode::WRITE, false);
//_tileCountNode->setAttribute(SGPropertyNode::WRITE, false);
}
void SGTerraSync::unbind()
@@ -1043,20 +961,7 @@ void SGTerraSync::unbind()
_terraRoot.clear();
_stalledNode.clear();
_activeNode.clear();
_cacheHits.clear();
_renderingRoot.clear();
_busyNode.clear();
_updateCountNode.clear();
_errorCountNode.clear();
_tileCountNode.clear();
_cacheHitsNode.clear();
_transferRateBytesSecNode.clear();
_pendingKbytesNode.clear();
_downloadedKBtesNode.clear();
_enabledNode.clear();
_availableNode.clear();
_maxErrorsNode.clear();
}
void SGTerraSync::update(double)
@@ -1064,21 +969,16 @@ void SGTerraSync::update(double)
auto enabled = _enabledNode->getBoolValue();
auto worker_running = _workerThread->isRunning();
// hold enabled false until retry time passes
if (enabled && (_retryTime > SGTimeStamp::now())) {
enabled = false;
}
// see if the enabled status has changed; and if so take the appropriate action.
if (enabled && !worker_running)
{
reinit();
SG_LOG(SG_TERRASYNC, SG_MANDATORY_INFO, "Terrasync started");
SG_LOG(SG_TERRASYNC, SG_ALERT, "Terrasync started");
}
else if (!enabled && worker_running)
{
reinit();
SG_LOG(SG_TERRASYNC, SG_MANDATORY_INFO, "Terrasync stopped");
SG_LOG(SG_TERRASYNC, SG_ALERT, "Terrasync stopped");
}
TerrasyncThreadState copiedState(_workerThread->threadsafeCopyState());
@@ -1094,31 +994,35 @@ void SGTerraSync::update(double)
_stalledNode->setBoolValue(_workerThread->isStalled());
_activeNode->setBoolValue(worker_running);
int allowedErrors = _maxErrorsNode->getIntValue();
if (worker_running && (copiedState._consecutive_errors >= allowedErrors)) {
_workerThread->stop();
_retryBackOffSeconds = std::min(_retryBackOffSeconds + 60, 60u * 15);
const int seconds = static_cast<int>(sg_random() * _retryBackOffSeconds);
_retryTime = SGTimeStamp::now() + SGTimeStamp::fromSec(seconds);
SG_LOG(SG_TERRASYNC, SG_ALERT, "Terrasync paused due to " << copiedState._consecutive_errors << " consecutive errors during sync; will retry in " << seconds << " seconds.");
}
while (_workerThread->hasNewTiles())
{
// ensure they are popped
_workerThread->getNewTile();
}
SyncItem next = _workerThread->getNewTile();
if ((next._type == SyncItem::Tile) || (next._type == SyncItem::AIData)) {
_activeTileDirs.erase(next._dir);
}
} // of freshly synced items
}
bool SGTerraSync::isIdle() {return _workerThread->isIdle();}
void SGTerraSync::syncAirportsModels()
{
SyncItem w("Airports", SyncItem::AirportData);
SyncItem a("Models", SyncItem::SharedModels);
_workerThread->request(w);
_workerThread->request(a);
static const char* bounds = "MZAJKL"; // airport sync order: K-L, A-J, M-Z
// note "request" method uses LIFO order, i.e. processes most recent request first
for( unsigned i = 0; i < strlen(bounds)/2; i++ )
{
for ( char synced_other = bounds[2*i]; synced_other <= bounds[2*i+1]; synced_other++ )
{
ostringstream dir;
dir << "Airports/" << synced_other;
SyncItem w(dir.str(), SyncItem::AirportData);
_workerThread->request( w );
}
}
SyncItem w("Models", SyncItem::SharedModels);
_workerThread->request( w );
}
string_list SGTerraSync::getSceneryPathSuffixes() const
@@ -1142,16 +1046,17 @@ string_list SGTerraSync::getSceneryPathSuffixes() const
void SGTerraSync::syncAreaByPath(const std::string& aPath)
{
if (!_workerThread->isRunning()) {
return;
}
string_list scenerySuffixes = getSceneryPathSuffixes();
string_list::const_iterator it = scenerySuffixes.begin();
for (const auto& suffix : getSceneryPathSuffixes()) {
const auto dir = suffix + "/" + aPath;
if (_workerThread->isDirActive(dir)) {
for (; it != scenerySuffixes.end(); ++it)
{
std::string dir = *it + "/" + aPath;
if (_activeTileDirs.find(dir) != _activeTileDirs.end()) {
continue;
}
_activeTileDirs.insert(dir);
SyncItem w(dir, SyncItem::Tile);
_workerThread->request( w );
}
@@ -1170,9 +1075,13 @@ bool SGTerraSync::isTileDirPending(const std::string& sceneryDir) const
return false;
}
for (const auto& suffix : getSceneryPathSuffixes()) {
const auto s = suffix + "/" + sceneryDir;
if (_workerThread->isDirActive(s)) {
string_list scenerySuffixes = getSceneryPathSuffixes();
string_list::const_iterator it = scenerySuffixes.begin();
for (; it != scenerySuffixes.end(); ++it)
{
string s = *it + "/" + sceneryDir;
if (_activeTileDirs.find(s) != _activeTileDirs.end()) {
return true;
}
}
@@ -1182,16 +1091,14 @@ bool SGTerraSync::isTileDirPending(const std::string& sceneryDir) const
void SGTerraSync::scheduleDataDir(const std::string& dataDir)
{
if (!_workerThread->isRunning()) {
return;
}
if (_workerThread->isDirActive(dataDir)) {
if (_activeTileDirs.find(dataDir) != _activeTileDirs.end()) {
return;
}
_activeTileDirs.insert(dataDir);
SyncItem w(dataDir, SyncItem::AIData);
_workerThread->request( w );
}
bool SGTerraSync::isDataDirPending(const std::string& dataDir) const
@@ -1200,7 +1107,7 @@ bool SGTerraSync::isDataDirPending(const std::string& dataDir) const
return false;
}
return _workerThread->isDirActive(dataDir);
return (_activeTileDirs.find(dataDir) != _activeTileDirs.end());
}
void SGTerraSync::reposition()

View File

@@ -107,7 +107,6 @@ private:
SGPropertyNode_ptr _transferRateBytesSecNode;
SGPropertyNode_ptr _pendingKbytesNode;
SGPropertyNode_ptr _downloadedKBtesNode;
SGPropertyNode_ptr _maxErrorsNode;
// we manually bind+init TerraSync during early startup
// to get better overlap of slow operations (Shared Models sync
@@ -118,10 +117,8 @@ private:
simgear::TiedPropertyList _tiedProperties;
BufferedLogCallback* _log;
/// if we disabled TerraSync due to errors, this is the time at which we will restart it
/// automatically.
SGTimeStamp _retryTime;
unsigned int _retryBackOffSeconds = 0;
typedef std::set<std::string> string_set;
string_set _activeTileDirs;
};
}

View File

@@ -6,6 +6,7 @@ set(HEADERS
xmlsound.hxx
soundmgr.hxx
filters.hxx
readwav.hxx
)
set(SOURCES
@@ -13,6 +14,7 @@ set(SOURCES
sample_group.cxx
xmlsound.cxx
filters.cxx
readwav.cxx
)
if (USE_AEONWAVE)
@@ -20,12 +22,8 @@ if (USE_AEONWAVE)
soundmgr_aeonwave.cxx
)
else()
set(HEADERS ${HEADERS}
readwav.hxx
)
set(SOURCES ${SOURCES}
soundmgr_openal.cxx
readwav.cxx
)
endif()

View File

@@ -380,8 +380,7 @@ namespace simgear
ALvoid* loadWAVFromFile(const SGPath& path, unsigned int& format, ALsizei& size, ALfloat& freqf, unsigned int& block_align)
{
if (!path.exists()) {
SG_LOG(SG_IO, SG_DEV_ALERT, "loadWAVFromFile: file not found:" << path);
return nullptr;
throw sg_io_exception("loadWAVFromFile: file not found", path);
}
Buffer b;
@@ -396,15 +395,13 @@ ALvoid* loadWAVFromFile(const SGPath& path, unsigned int& format, ALsizei& size,
fd = gzopen(ps.c_str(), "rb");
#endif
if (!fd) {
SG_LOG(SG_IO, SG_DEV_ALERT, "loadWAVFromFile: unable to open file:" << path);
return nullptr;
throw sg_io_exception("loadWAVFromFile: unable to open file", path);
}
try {
loadWavFile(fd, &b);
} catch (sg_exception& e) {
SG_LOG(SG_IO, SG_DEV_ALERT, "loadWAVFromFile:" << e.getFormattedMessage() << "\nfor: " << path);
return nullptr;
throw sg_io_exception(e.getFormattedMessage() + "\nfor: " + path.str());
}
ALvoid* data = b.data;

View File

@@ -594,7 +594,6 @@ unsigned int SGSoundMgr::request_buffer(SGSoundSample *sample)
case SG_SAMPLE_STEREO8:
SG_LOG(SG_SOUND, SG_POPUP, "Stereo sound detected:\n" << sample->get_sample_name() << "\nUse two separate mono files instead if required.");
format = AL_FORMAT_STEREO8;
break;
default:
SG_LOG(SG_SOUND, SG_ALERT, "unsupported audio format");
return buffer;
@@ -813,14 +812,13 @@ bool SGSoundMgr::load( const std::string &samplepath,
auto data = simgear::loadWAVFromFile(samplepath, format, size, freqf, blocksz);
freq = (ALsizei)freqf;
if (!data) {
return false;
if (data == nullptr) {
throw sg_io_exception("Failed to load wav file", sg_location(samplepath));
}
if (format == AL_FORMAT_STEREO8 || format == AL_FORMAT_STEREO16) {
free(data);
SG_LOG(SG_IO, SG_DEV_ALERT, "Warning: STEREO files are not supported for 3D audio effects: " << samplepath);
return false;
throw sg_io_exception("Warning: STEREO files are not supported for 3D audio effects: " + samplepath);
}
*dbuf = (void *)data;

View File

@@ -84,7 +84,7 @@ SGXmlSound::~SGXmlSound()
_pitch.clear();
}
bool
void
SGXmlSound::init( SGPropertyNode *root,
SGPropertyNode *node,
SGSampleGroup *sgrp,
@@ -315,8 +315,8 @@ SGXmlSound::init( SGPropertyNode *root,
string soundFileStr = node->getStringValue("path", "");
_sample = new SGSoundSample(soundFileStr.c_str(), path);
if (!_sample->file_path().exists()) {
SG_LOG(SG_SOUND, SG_WARN, "XML sound: couldn't find file: '" + soundFileStr + "'");
return false;
throw sg_io_exception("XML sound: couldn't find file: '" + soundFileStr + "'");
return;
}
_sample->set_relative_position( offset_pos );
@@ -328,8 +328,6 @@ SGXmlSound::init( SGPropertyNode *root,
_sample->set_volume( v );
_sample->set_pitch( p );
_sgrp->add( _sample, _name );
return true;
}
void
@@ -416,11 +414,9 @@ SGXmlSound::update (double dt)
double v = 1.0;
if (_volume[i].expr) {
double expression_value = _volume[i].expr->getValue(nullptr);
if (expression_value >= 0)
volume *= expression_value;
expr = true;
continue;
v = _volume[i].expr->getValue(nullptr);
expr = true;
continue;
}
if (_volume[i].prop) {
@@ -441,23 +437,19 @@ SGXmlSound::update (double dt)
if (_volume[i].max && (v > _volume[i].max))
v = _volume[i].max;
else if (v < _volume[i].min)
v = _volume[i].min;
if (_volume[i].subtract) // Hack!
volume = _volume[i].offset - v;
if (_volume[i].subtract){ // Hack!
v = v + _volume[i].offset;
if (v >= 0)
volume = volume * v;
}
else {
if (v >= 0) {
volume_offset += _volume[i].offset;
volume = volume * v;
}
volume_offset += _volume[i].offset;
volume *= v;
}
}
//
// Update the pitch
//
@@ -470,9 +462,9 @@ SGXmlSound::update (double dt)
double p = 1.0;
if (_pitch[i].expr) {
pitch *= _pitch[i].expr->getValue(nullptr);
expr = true;
continue;
p = _pitch[i].expr->getValue(nullptr);
expr = true;
continue;
}
if (_pitch[i].prop) {

View File

@@ -107,7 +107,7 @@ public:
* @param avionics A pointer to the pre-initialized avionics sample group.
* @param path The path where the audio files remain.
*/
virtual bool init( SGPropertyNode *root,
virtual void init( SGPropertyNode *root,
SGPropertyNode *child,
SGSampleGroup *sgrp,
SGSampleGroup *avionics,

View File

@@ -18,16 +18,24 @@
#include <simgear/structure/exception.hxx>
SGBinding::SGBinding()
: _arg(new SGPropertyNode)
: _command(0),
_arg(new SGPropertyNode),
_setting(0)
{
}
SGBinding::SGBinding(const std::string& commandName)
: _command(0),
_arg(0),
_setting(0)
{
_command_name = commandName;
}
SGBinding::SGBinding(const SGPropertyNode* node, SGPropertyNode* root)
: _command(0),
_arg(0),
_setting(0)
{
read(node, root);
}
@@ -35,8 +43,8 @@ SGBinding::SGBinding(const SGPropertyNode* node, SGPropertyNode* root)
void
SGBinding::clear()
{
_command = NULL;
_arg.clear();
_root.clear();
_setting.clear();
}
@@ -49,12 +57,14 @@ SGBinding::read(const SGPropertyNode* node, SGPropertyNode* root)
_command_name = node->getStringValue("command", "");
if (_command_name.empty()) {
SG_LOG(SG_INPUT, SG_DEV_ALERT, "No command supplied for binding.");
SG_LOG(SG_INPUT, SG_WARN, "No command supplied for binding.");
_command = 0;
}
_arg = const_cast<SGPropertyNode*>(node);
_root = const_cast<SGPropertyNode*>(root);
_setting.clear();
_setting = 0;
}
void
@@ -68,29 +78,30 @@ SGBinding::fire() const
void
SGBinding::innerFire () const
{
auto cmd = SGCommandMgr::instance()->getCommand(_command_name);
if (!cmd) {
SG_LOG(SG_INPUT, SG_WARN, "No command found for binding:" << _command_name);
return;
}
try {
if (!(*cmd)(_arg, _root)) {
SG_LOG(SG_INPUT, SG_ALERT, "Failed to execute command " << _command_name);
}
} catch (sg_exception& e) {
if (_command == 0)
_command = SGCommandMgr::instance()->getCommand(_command_name);
if (_command == 0) {
SG_LOG(SG_INPUT, SG_WARN, "No command attached to binding:" << _command_name);
} else {
try {
if (!(*_command)(_arg, _root)) {
SG_LOG(SG_INPUT, SG_ALERT, "Failed to execute command "
<< _command_name);
}
} catch (sg_exception& e) {
SG_LOG(SG_GENERAL, SG_ALERT, "command '" << _command_name << "' failed with exception\n"
<< "\tmessage:" << e.getMessage() << " (from " << e.getOrigin() << ")");
}
<< "\tmessage:" << e.getMessage() << " (from " << e.getOrigin() << ")");
}
}
}
void
SGBinding::fire (SGPropertyNode* params) const
{
if (test()) {
if (params != nullptr) {
copyProperties(params, _arg);
}
if (params != NULL) {
copyProperties(params, _arg);
}
innerFire();
}
@@ -111,9 +122,8 @@ SGBinding::fire (double setting) const
if (test()) {
// A value is automatically added to
// the args
if (!_setting) { // save the setting node for efficiency
_setting = _arg->getChild("setting", 0, true);
}
if (_setting == 0) // save the setting node for efficiency
_setting = _arg->getChild("setting", 0, true);
_setting->setDoubleValue(setting);
innerFire();
}

View File

@@ -77,6 +77,16 @@ public:
*/
const std::string &getCommandName () const { return _command_name; }
/**
* Get the command itself.
*
* @return The command associated with this binding, or 0 if none
* is present.
*/
SGCommandMgr::Command* getCommand () const { return _command; }
/**
* Get the argument that will be passed to the command.
*
@@ -130,6 +140,7 @@ private:
SGBinding (const SGBinding &binding);
std::string _command_name;
mutable SGCommandMgr::Command* _command;
mutable SGPropertyNode_ptr _arg;
mutable SGPropertyNode_ptr _setting;
mutable SGPropertyNode_ptr _root;

View File

@@ -13,8 +13,6 @@
#include <simgear/misc/sg_path.hxx>
static ThrowCallback static_callback;
////////////////////////////////////////////////////////////////////////
// Implementation of sg_location class.
////////////////////////////////////////////////////////////////////////
@@ -51,7 +49,19 @@ sg_location::sg_location (const char* path, int line, int column)
setPath(path);
}
void sg_location::setPath(const char *path) {
sg_location::~sg_location ()
{
}
const char*
sg_location::getPath () const
{
return _path;
}
void
sg_location::setPath (const char* path)
{
if (path) {
strncpy(_path, path, max_path);
_path[max_path -1] = '\0';
@@ -60,9 +70,17 @@ void sg_location::setPath(const char *path) {
}
}
const char *sg_location::getPath() const { return _path; }
int
sg_location::getLine () const
{
return _line;
}
int sg_location::getLine() const { return _line; }
void
sg_location::setLine (int line)
{
_line = line;
}
int
sg_location::getColumn () const
@@ -70,6 +88,11 @@ sg_location::getColumn () const
return _column;
}
void
sg_location::setColumn (int column)
{
_column = column;
}
int
sg_location::getByte () const
@@ -77,6 +100,12 @@ sg_location::getByte () const
return _byte;
}
void
sg_location::setByte (int byte)
{
_byte = byte;
}
std::string
sg_location::asString () const
{
@@ -97,8 +126,8 @@ sg_location::asString () const
return out.str();
}
bool sg_location::isValid() const { return strlen(_path) > 0; }
////////////////////////////////////////////////////////////////////////
// Implementation of sg_throwable class.
////////////////////////////////////////////////////////////////////////
@@ -109,14 +138,10 @@ sg_throwable::sg_throwable ()
_origin[0] = '\0';
}
sg_throwable::sg_throwable(const char *message, const char *origin,
const sg_location &loc) {
sg_throwable::sg_throwable (const char* message, const char* origin)
{
setMessage(message);
setOrigin(origin);
if (static_callback) {
static_callback(_message, _origin, loc);
}
}
sg_throwable::~sg_throwable ()
@@ -203,13 +228,16 @@ sg_exception::sg_exception ()
{
}
sg_exception::sg_exception(const char *message, const char *origin,
const sg_location &loc)
: sg_throwable(message, origin, loc) {}
sg_exception::sg_exception (const char* message, const char* origin)
: sg_throwable(message, origin)
{
}
sg_exception::sg_exception(const std::string &message,
const std::string &origin, const sg_location &loc)
: sg_throwable(message.c_str(), origin.c_str(), loc) {}
sg_exception::sg_exception( const std::string& message,
const std::string& origin )
: sg_throwable(message.c_str(), origin.c_str())
{
}
sg_exception::~sg_exception ()
{
@@ -229,10 +257,13 @@ sg_io_exception::sg_io_exception (const char* message, const char* origin)
{
}
sg_io_exception::sg_io_exception(const char *message,
const sg_location &location,
const char *origin)
: sg_exception(message, origin, location), _location(location) {}
sg_io_exception::sg_io_exception (const char* message,
const sg_location &location,
const char* origin)
: sg_exception(message, origin),
_location(location)
{
}
sg_io_exception::sg_io_exception( const std::string& message,
const std::string& origin )
@@ -240,10 +271,13 @@ sg_io_exception::sg_io_exception( const std::string& message,
{
}
sg_io_exception::sg_io_exception(const std::string &message,
const sg_location &location,
const std::string &origin)
: sg_exception(message, origin, location), _location(location) {}
sg_io_exception::sg_io_exception( const std::string& message,
const sg_location &location,
const std::string& origin )
: sg_exception(message, origin),
_location(location)
{
}
sg_io_exception::~sg_io_exception ()
{
@@ -348,9 +382,4 @@ sg_range_exception::sg_range_exception(const std::string& message,
sg_range_exception::~sg_range_exception ()
{
}
////////////////////////////////////////////////////////////////////////
void setThrowCallback(ThrowCallback cb) { static_callback = cb; }
// end of exception.cxx

View File

@@ -11,7 +11,6 @@
#define __SIMGEAR_MISC_EXCEPTION_HXX 1
#include <exception>
#include <functional>
#include <simgear/compiler.h>
#include <string>
@@ -32,20 +31,17 @@ public:
sg_location(const std::string& path, int line = -1, int column = -1);
sg_location(const SGPath& path, int line = -1, int column = -1);
explicit sg_location(const char* path, int line = -1, int column = -1);
~sg_location() = default;
const char *getPath() const;
int getLine() const;
int getColumn() const;
int getByte() const;
std::string asString() const;
bool isValid() const;
virtual ~sg_location();
virtual const char* getPath() const;
virtual void setPath (const char* path);
virtual int getLine () const;
virtual void setLine (int line);
virtual int getColumn () const;
virtual void setColumn (int column);
virtual int getByte () const;
virtual void setByte (int byte);
virtual std::string asString () const;
private:
void setPath(const char *p);
char _path[max_path];
int _line;
int _column;
@@ -61,9 +57,7 @@ class sg_throwable : public std::exception
public:
enum {MAX_TEXT_LEN = 1024};
sg_throwable ();
sg_throwable(const char *message, const char *origin = 0,
const sg_location &loc = {});
sg_throwable (const char* message, const char* origin = 0);
virtual ~sg_throwable ();
virtual const char* getMessage () const;
virtual const std::string getFormattedMessage () const;
@@ -92,7 +86,7 @@ class sg_error : public sg_throwable
public:
sg_error ();
sg_error (const char* message, const char* origin = 0);
sg_error(const std::string &message, const std::string &origin = {});
sg_error (const std::string& message, const std::string& origin = "");
virtual ~sg_error ();
};
@@ -115,10 +109,8 @@ class sg_exception : public sg_throwable
{
public:
sg_exception ();
sg_exception(const char *message, const char *origin = 0,
const sg_location &loc = {});
sg_exception(const std::string &message, const std::string & = {},
const sg_location &loc = {});
sg_exception (const char* message, const char* origin = 0);
sg_exception (const std::string& message, const std::string& = "");
virtual ~sg_exception ();
};
@@ -201,17 +193,6 @@ public:
virtual ~sg_range_exception ();
};
using ThrowCallback = std::function<void(
const char *message, const char *origin, const sg_location &loc)>;
/**
* @brief Specify a callback to be invoked when an exception is created.
*
* This is used to capture a stack-trace for our crash/error reporting system,
* if a callback is defined
*/
void setThrowCallback(ThrowCallback cb);
#endif
// end of exception.hxx

View File

@@ -272,35 +272,30 @@ template<class T>
class SGBlockingDeque
{
public:
using value_type = T;
using container_type = std::deque<T>;
/**
* Create a new SGBlockingDequeue.
*/
SGBlockingDeque() = default;
SGBlockingDeque() {}
/**
* Destroy this dequeue.
*/
~SGBlockingDeque() = default;
virtual ~SGBlockingDeque() {}
/**
*
*/
void clear()
{
std::lock_guard<std::mutex> g(mutex);
this->queue.clear();
virtual void clear() {
std::lock_guard<std::mutex> g(mutex);
this->queue.clear();
}
/**
*
*/
bool empty() const
{
std::lock_guard<std::mutex> g(mutex);
return this->queue.empty();
virtual bool empty() {
std::lock_guard<std::mutex> g(mutex);
return this->queue.empty();
}
/**
@@ -308,11 +303,10 @@ public:
*
* @param item The object to add.
*/
void push_front(const T& item)
{
std::lock_guard<std::mutex> g(mutex);
this->queue.push_front(item);
not_empty.signal();
virtual void push_front( const T& item ) {
std::lock_guard<std::mutex> g(mutex);
this->queue.push_front( item );
not_empty.signal();
}
/**
@@ -320,11 +314,10 @@ public:
*
* @param item The object to add.
*/
void push_back(const T& item)
{
std::lock_guard<std::mutex> g(mutex);
this->queue.push_back(item);
not_empty.signal();
virtual void push_back( const T& item ) {
std::lock_guard<std::mutex> g(mutex);
this->queue.push_back( item );
not_empty.signal();
}
/**
@@ -333,15 +326,14 @@ public:
*
* @return The next available object.
*/
T front() const
{
std::lock_guard<std::mutex> g(mutex);
virtual T front() {
std::lock_guard<std::mutex> g(mutex);
assert(this->queue.empty() != true);
//if (queue.empty()) throw ??
assert(this->queue.empty() != true);
//if (queue.empty()) throw ??
T item = this->queue.front();
return item;
T item = this->queue.front();
return item;
}
/**
@@ -350,19 +342,18 @@ public:
*
* @return The next available object.
*/
T pop_front()
{
std::lock_guard<std::mutex> g(mutex);
virtual T pop_front() {
std::lock_guard<std::mutex> g(mutex);
while (this->queue.empty())
not_empty.wait(mutex);
while (this->queue.empty())
not_empty.wait(mutex);
assert(this->queue.empty() != true);
//if (queue.empty()) throw ??
assert(this->queue.empty() != true);
//if (queue.empty()) throw ??
T item = this->queue.front();
this->queue.pop_front();
return item;
T item = this->queue.front();
this->queue.pop_front();
return item;
}
/**
@@ -371,19 +362,18 @@ public:
*
* @return The next available object.
*/
T pop_back()
{
std::lock_guard<std::mutex> g(mutex);
virtual T pop_back() {
std::lock_guard<std::mutex> g(mutex);
while (this->queue.empty())
not_empty.wait(mutex);
while (this->queue.empty())
not_empty.wait(mutex);
assert(this->queue.empty() != true);
//if (queue.empty()) throw ??
assert(this->queue.empty() != true);
//if (queue.empty()) throw ??
T item = this->queue.back();
this->queue.pop_back();
return item;
T item = this->queue.back();
this->queue.pop_back();
return item;
}
/**
@@ -391,9 +381,8 @@ public:
*
* @return Size of queue.
*/
size_t size() const
{
std::lock_guard<std::mutex> g(mutex);
virtual size_t size() {
std::lock_guard<std::mutex> g(mutex);
return this->queue.size();
}
@@ -402,19 +391,12 @@ public:
while (this->queue.empty())
not_empty.wait(mutex);
}
container_type copy() const
{
std::lock_guard<std::mutex> g(mutex);
return queue;
}
private:
/**
* Mutex to serialise access.
*/
mutable std::mutex mutex;
std::mutex mutex;
/**
* Condition to signal when queue not empty.
@@ -427,7 +409,7 @@ private:
SGBlockingDeque& operator=( const SGBlockingDeque& );
protected:
container_type queue;
std::deque<T> queue;
};
#endif // SGQUEUE_HXX_INCLUDED

1
version Normal file
View File

@@ -0,0 +1 @@
2020.1.3