From 29f0631a2c322d5e86ec2e64b7940d966d7b00f0 Mon Sep 17 00:00:00 2001 From: krzys-h Date: Sun, 10 Jul 2016 14:56:34 +0200 Subject: [PATCH 01/56] Asynchronous sound/music loading This improves the loading time a lot. Time from starting the app to opening game window decreased by almost 5 seconds (it's almost instant now). Mission loading times are significantly better too. As a bonus, the playmusic() CBot function doesn't hang the game if you don't preload the files with CacheAudio in scene file. --- src/CMakeLists.txt | 2 + src/app/app.cpp | 18 +++- src/common/thread/resource_owning_thread.h | 15 ++- src/common/thread/sdl_cond_wrapper.h | 12 +++ src/common/thread/sdl_mutex_wrapper.h | 10 ++ src/common/thread/thread.h | 77 ++++++++++++++ src/common/thread/worker_thread.h | 92 +++++++++++++++++ src/sound/oalsound/alsound.cpp | 113 +++++++++------------ src/sound/oalsound/alsound.h | 11 +- src/sound/sound.cpp | 28 +---- src/sound/sound.h | 26 ++--- src/ui/mainui.cpp | 3 +- 12 files changed, 289 insertions(+), 118 deletions(-) create mode 100644 src/common/thread/thread.h create mode 100644 src/common/thread/worker_thread.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bc6f12ad..4d5a6409 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -151,6 +151,8 @@ set(BASE_SOURCES common/thread/resource_owning_thread.h common/thread/sdl_cond_wrapper.h common/thread/sdl_mutex_wrapper.h + common/thread/thread.h + common/thread/worker_thread.h graphics/core/color.cpp graphics/core/color.h graphics/core/device.h diff --git a/src/app/app.cpp b/src/app/app.cpp index d34532f7..6241ef08 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -34,6 +34,8 @@ #include "common/resources/resourcemanager.h" +#include "common/thread/thread.h" + #include "graphics/core/nulldevice.h" #include "graphics/opengl/glutil.h" @@ -514,8 +516,6 @@ bool CApplication::Create() #endif m_sound->Create(); - m_sound->CacheAll(); - m_sound->CacheCommonMusic(); GetLogger()->Info("CApplication created successfully\n"); @@ -685,6 +685,20 @@ bool CApplication::Create() // Create the robot application. m_controller = MakeUnique(); + CThread musicLoadThread([this]() { + GetLogger()->Debug("Cache sounds...\n"); + SystemTimeStamp* musicLoadStart = m_systemUtils->CreateTimeStamp(); + m_systemUtils->GetCurrentTimeStamp(musicLoadStart); + + m_sound->CacheAll(); + + SystemTimeStamp* musicLoadEnd = m_systemUtils->CreateTimeStamp(); + m_systemUtils->GetCurrentTimeStamp(musicLoadEnd); + float musicLoadTime = m_systemUtils->TimeStampDiff(musicLoadStart, musicLoadEnd, STU_MSEC); + GetLogger()->Debug("Sound loading took %.2f ms\n", musicLoadTime); + }, "Sound loading thread"); + musicLoadThread.Start(); + if (m_runSceneCategory == LevelCategory::Max) m_controller->StartApp(); else diff --git a/src/common/thread/resource_owning_thread.h b/src/common/thread/resource_owning_thread.h index 0e4c2006..04237961 100644 --- a/src/common/thread/resource_owning_thread.h +++ b/src/common/thread/resource_owning_thread.h @@ -59,6 +59,11 @@ public: m_name(name) {} + ~CResourceOwningThread() + { + SDL_DetachThread(m_thread); + } + void Start() { CSDLMutexWrapper mutex; @@ -74,7 +79,7 @@ public: SDL_LockMutex(*mutex); - SDL_CreateThread(Run, !m_name.empty() ? m_name.c_str() : nullptr, reinterpret_cast(&data)); + m_thread = SDL_CreateThread(Run, !m_name.empty() ? m_name.c_str() : nullptr, reinterpret_cast(&data)); while (!condition) { @@ -84,6 +89,13 @@ public: SDL_UnlockMutex(*mutex); } + void Join() + { + if (m_thread == nullptr) return; + SDL_WaitThread(m_thread, nullptr); + m_thread = nullptr; + } + private: static int Run(void* data) { @@ -117,4 +129,5 @@ private: ThreadFunctionPtr m_threadFunction; ResourceUPtr m_resource; std::string m_name; + SDL_Thread* m_thread = nullptr; }; diff --git a/src/common/thread/sdl_cond_wrapper.h b/src/common/thread/sdl_cond_wrapper.h index 5b09d2f3..8c8be5ee 100644 --- a/src/common/thread/sdl_cond_wrapper.h +++ b/src/common/thread/sdl_cond_wrapper.h @@ -19,6 +19,8 @@ #pragma once +#include "common/thread/sdl_mutex_wrapper.h" + #include /** @@ -45,6 +47,16 @@ public: return m_cond; } + void Signal() + { + SDL_CondSignal(m_cond); + } + + void Wait(SDL_mutex* mutex) + { + SDL_CondWait(m_cond, mutex); + } + private: SDL_cond* m_cond; }; diff --git a/src/common/thread/sdl_mutex_wrapper.h b/src/common/thread/sdl_mutex_wrapper.h index bd7d9075..59afc0aa 100644 --- a/src/common/thread/sdl_mutex_wrapper.h +++ b/src/common/thread/sdl_mutex_wrapper.h @@ -45,6 +45,16 @@ public: return m_mutex; } + void Lock() + { + SDL_LockMutex(m_mutex); + } + + void Unlock() + { + SDL_UnlockMutex(m_mutex); + } + private: SDL_mutex* m_mutex; }; diff --git a/src/common/thread/thread.h b/src/common/thread/thread.h new file mode 100644 index 00000000..d0b6b873 --- /dev/null +++ b/src/common/thread/thread.h @@ -0,0 +1,77 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * 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 3 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, see http://gnu.org/licenses + */ + +#pragma once + +#include "common/make_unique.h" + +#include "common/thread/resource_owning_thread.h" + +#include +#include +#include + +/** + * \class CThread + * \brief Wrapper for using SDL_thread with std::function + */ +class CThread +{ +public: + using ThreadFunctionPtr = std::function; + +private: + struct ThreadData + { + ThreadFunctionPtr func; + }; + +public: + CThread(ThreadFunctionPtr func, std::string name = "") + : m_func(std::move(func)) + , m_name(name) + {} + + void Start() + { + std::unique_ptr data = MakeUnique(); + data->func = m_func; + m_thread = MakeUnique>(Run, std::move(data), m_name); + m_thread->Start(); + } + + void Join() + { + if (!m_thread) return; + m_thread->Join(); + } + + CThread(const CThread&) = delete; + CThread& operator=(const CThread&) = delete; + +private: + static void Run(std::unique_ptr data) + { + data->func(); + } + + std::unique_ptr> m_thread; + ThreadFunctionPtr m_func; + std::string m_name; +}; diff --git a/src/common/thread/worker_thread.h b/src/common/thread/worker_thread.h new file mode 100644 index 00000000..aa2ce3e4 --- /dev/null +++ b/src/common/thread/worker_thread.h @@ -0,0 +1,92 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * 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 3 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, see http://gnu.org/licenses + */ + +#pragma once + +#include "common/make_unique.h" + +#include "common/thread/sdl_cond_wrapper.h" +#include "common/thread/sdl_mutex_wrapper.h" +#include "common/thread/thread.h" + +#include +#include +#include + +/** + * \class CWorkerThread + * \brief Thread that runs functions, one at a time + */ +class CWorkerThread +{ +public: + using ThreadFunctionPtr = std::function; + +public: + CWorkerThread(std::string name = "") + : m_thread(std::bind(&CWorkerThread::Run, this), name) + { + m_thread.Start(); + } + + ~CWorkerThread() + { + m_mutex.Lock(); + m_running = false; + m_cond.Signal(); + m_mutex.Unlock(); + m_thread.Join(); + } + + void Start(ThreadFunctionPtr func) + { + m_mutex.Lock(); + m_queue.push(func); + m_cond.Signal(); + m_mutex.Unlock(); + } + + CWorkerThread(const CWorkerThread&) = delete; + CWorkerThread& operator=(const CWorkerThread&) = delete; + +private: + void Run() + { + m_mutex.Lock(); + while (true) + { + while (m_queue.empty() && m_running) + { + m_cond.Wait(*m_mutex); + } + if (!m_running) break; + + ThreadFunctionPtr func = m_queue.front(); + m_queue.pop(); + func(); + } + m_mutex.Unlock(); + } + + CThread m_thread; + CSDLMutexWrapper m_mutex; + CSDLCondWrapper m_cond; + bool m_running = true; + std::queue m_queue; +}; diff --git a/src/sound/oalsound/alsound.cpp b/src/sound/oalsound/alsound.cpp index cfa335d1..f51887b1 100644 --- a/src/sound/oalsound/alsound.cpp +++ b/src/sound/oalsound/alsound.cpp @@ -32,7 +32,8 @@ CALSound::CALSound() m_musicVolume(1.0f), m_channelsLimit(2048), m_device{}, - m_context{} + m_context{}, + m_thread("Music loading thread") { } @@ -144,18 +145,19 @@ bool CALSound::Cache(SoundType sound, const std::string &filename) return false; } -bool CALSound::CacheMusic(const std::string &filename) +void CALSound::CacheMusic(const std::string &filename) { - if (m_music.find(filename) == m_music.end()) + m_thread.Start([this, filename]() { - auto buffer = MakeUnique(); - if (buffer->LoadFromFile(filename, static_cast(-1))) + if (m_music.find(filename) == m_music.end()) { - m_music[filename] = std::move(buffer); - return true; + auto buffer = MakeUnique(); + if (buffer->LoadFromFile(filename, static_cast(-1))) + { + m_music[filename] = std::move(buffer); + } } - } - return false; + }); } bool CALSound::IsCached(SoundType sound) @@ -572,53 +574,54 @@ void CALSound::SetListener(const Math::Vector &eye, const Math::Vector &lookat) alListenerfv(AL_ORIENTATION, orientation); } -bool CALSound::PlayMusic(const std::string &filename, bool repeat, float fadeTime) +void CALSound::PlayMusic(const std::string &filename, bool repeat, float fadeTime) { if (!m_enabled) { - return false; + return; } - CBuffer *buffer = nullptr; - - // check if we have music in cache - if (m_music.find(filename) == m_music.end()) + m_thread.Start([this, filename, repeat, fadeTime]() { - GetLogger()->Debug("Music %s was not cached!\n", filename.c_str()); + CBuffer* buffer = nullptr; - auto newBuffer = MakeUnique(); - buffer = newBuffer.get(); - if (!newBuffer->LoadFromFile(filename, static_cast(-1))) + // check if we have music in cache + if (m_music.find(filename) == m_music.end()) { - return false; + GetLogger()->Debug("Music %s was not cached!\n", filename.c_str()); + + auto newBuffer = MakeUnique(); + buffer = newBuffer.get(); + if (!newBuffer->LoadFromFile(filename, static_cast(-1))) + { + return; + } + m_music[filename] = std::move(newBuffer); + } + else + { + GetLogger()->Debug("Music loaded from cache\n"); + buffer = m_music[filename].get(); } - m_music[filename] = std::move(newBuffer); - } - else - { - GetLogger()->Debug("Music loaded from cache\n"); - buffer = m_music[filename].get(); - } - if (m_currentMusic) - { - OldMusic old; - old.music = std::move(m_currentMusic); - old.fadeTime = fadeTime; - old.currentTime = 0.0f; - m_oldMusic.push_back(std::move(old)); - } + if (m_currentMusic) + { + OldMusic old; + old.music = std::move(m_currentMusic); + old.fadeTime = fadeTime; + old.currentTime = 0.0f; + m_oldMusic.push_back(std::move(old)); + } - m_currentMusic = MakeUnique(); - m_currentMusic->SetBuffer(buffer); - m_currentMusic->SetVolume(m_musicVolume); - m_currentMusic->SetLoop(repeat); - m_currentMusic->Play(); - - return true; + m_currentMusic = MakeUnique(); + m_currentMusic->SetBuffer(buffer); + m_currentMusic->SetVolume(m_musicVolume); + m_currentMusic->SetLoop(repeat); + m_currentMusic->Play(); + }); } -bool CALSound::PlayPauseMusic(const std::string &filename, bool repeat) +void CALSound::PlayPauseMusic(const std::string &filename, bool repeat) { if (m_previousMusic.fadeTime > 0.0f) { @@ -640,7 +643,7 @@ bool CALSound::PlayPauseMusic(const std::string &filename, bool repeat) m_previousMusic.currentTime = 0.0f; } } - return PlayMusic(filename, repeat); + PlayMusic(filename, repeat); } void CALSound::StopPauseMusic() @@ -662,18 +665,6 @@ void CALSound::StopPauseMusic() } } -bool CALSound::RestartMusic() -{ - if (!m_enabled || m_currentMusic == nullptr) - { - return false; - } - - m_currentMusic->Stop(); - m_currentMusic->Play(); - return true; -} - void CALSound::StopMusic(float fadeTime) { if (!m_enabled || m_currentMusic == nullptr) @@ -698,16 +689,6 @@ bool CALSound::IsPlayingMusic() return m_currentMusic->IsPlaying(); } -void CALSound::SuspendMusic() -{ - if (!m_enabled || m_currentMusic == nullptr) - { - return; - } - - m_currentMusic->Stop(); -} - bool CALSound::CheckChannel(int &channel) { int id = (channel >> 16) & 0xffff; diff --git a/src/sound/oalsound/alsound.h b/src/sound/oalsound/alsound.h index e4656e11..03d5bd49 100644 --- a/src/sound/oalsound/alsound.h +++ b/src/sound/oalsound/alsound.h @@ -26,6 +26,8 @@ #include "sound/sound.h" +#include "common/thread/worker_thread.h" + #include "sound/oalsound/buffer.h" #include "sound/oalsound/channel.h" #include "sound/oalsound/check.h" @@ -83,7 +85,7 @@ public: bool Create() override; bool Cache(SoundType, const std::string &) override; - bool CacheMusic(const std::string &) override; + void CacheMusic(const std::string &) override; bool IsCached(SoundType) override; bool IsCachedMusic(const std::string &) override; @@ -106,12 +108,10 @@ public: bool StopAll() override; bool MuteAll(bool mute) override; - bool PlayMusic(const std::string &filename, bool repeat, float fadeTime=2.0f) override; - bool RestartMusic() override; - void SuspendMusic() override; + void PlayMusic(const std::string &filename, bool repeat, float fadeTime = 2.0f) override; void StopMusic(float fadeTime=2.0f) override; bool IsPlayingMusic() override; - bool PlayPauseMusic(const std::string &filename, bool repeat) override; + void PlayPauseMusic(const std::string &filename, bool repeat) override; void StopPauseMusic() override; private: @@ -134,4 +134,5 @@ private: OldMusic m_previousMusic; Math::Vector m_eye; Math::Vector m_lookat; + CWorkerThread m_thread; }; diff --git a/src/sound/sound.cpp b/src/sound/sound.cpp index 3640c585..e5b29291 100644 --- a/src/sound/sound.cpp +++ b/src/sound/sound.cpp @@ -52,22 +52,13 @@ void CSoundInterface::CacheAll() } } -void CSoundInterface::CacheCommonMusic() -{ - CacheMusic("music/Intro1.ogg"); - CacheMusic("music/Intro2.ogg"); - CacheMusic("music/music010.ogg"); - CacheMusic("music/music011.ogg"); -} - bool CSoundInterface::Cache(SoundType sound, const std::string &file) { return true; } -bool CSoundInterface::CacheMusic(const std::string &file) +void CSoundInterface::CacheMusic(const std::string &file) { - return true; } bool CSoundInterface::IsCached(SoundType sound) @@ -156,17 +147,7 @@ bool CSoundInterface::MuteAll(bool mute) return true; } -bool CSoundInterface::PlayMusic(const std::string &filename, bool repeat, float fadeTime) -{ - return true; -} - -bool CSoundInterface::RestartMusic() -{ - return true; -} - -void CSoundInterface::SuspendMusic() +void CSoundInterface::PlayMusic(const std::string &filename, bool repeat, float fadeTime) { } @@ -176,12 +157,11 @@ void CSoundInterface::StopMusic(float fadeTime) bool CSoundInterface::IsPlayingMusic() { - return true; + return false; } -bool CSoundInterface::PlayPauseMusic(const std::string &filename, bool repeat) +void CSoundInterface::PlayPauseMusic(const std::string &filename, bool repeat) { - return true; } void CSoundInterface::StopPauseMusic() diff --git a/src/sound/sound.h b/src/sound/sound.h index 3799d6f9..a2dce312 100644 --- a/src/sound/sound.h +++ b/src/sound/sound.h @@ -72,9 +72,6 @@ public: */ void CacheAll(); - /** Function called to add all music files to list */ - void CacheCommonMusic(); - /** Function called to cache sound effect file. * This function is called by plugin interface for each file. * \param sound - id of a file, will be used to identify sound files @@ -85,10 +82,10 @@ public: /** Function called to cache music file. * This function is called by CRobotMain for each file used in the mission. + * This function is executed asynchronously * \param file - file to load - * \return return true on success */ - virtual bool CacheMusic(const std::string &file); + virtual void CacheMusic(const std::string &file); /** Function to check if sound effect file was cached. * \param sound - id of a sound effect file @@ -205,22 +202,12 @@ public: virtual bool MuteAll(bool mute); /** Start playing music + * This function is executed asynchronously * \param filename - name of file to play * \param repeat - repeat playing - * \param fadeTime - time of transition between music - * \return return true on success + * \param fadeTime - time of transition between music, 0 to disable */ - virtual bool PlayMusic(const std::string &filename, bool repeat, float fadeTime=2.0f); - - /** Restart music - * \return return true on success - */ - virtual bool RestartMusic(); - - /** Susspend playing music - * \return nothing - */ - virtual void SuspendMusic(); + virtual void PlayMusic(const std::string &filename, bool repeat, float fadeTime = 2.0f); /** Stop playing music * \return nothing @@ -233,11 +220,12 @@ public: virtual bool IsPlayingMusic(); /** Start playing pause music + * This function is executed asynchronously * \param filename - name of file to play * \param repeat - repeat playing * \return return true on success */ - virtual bool PlayPauseMusic(const std::string &filename, bool repeat); + virtual void PlayPauseMusic(const std::string &filename, bool repeat); /** Stop playing pause music and return to the mission music * \return nothing diff --git a/src/ui/mainui.cpp b/src/ui/mainui.cpp index f373d692..503cc49f 100644 --- a/src/ui/mainui.cpp +++ b/src/ui/mainui.cpp @@ -213,9 +213,10 @@ void CMainUserInterface::ChangePhase(Phase phase) if ( IsMainMenuPhase(m_phase) ) { - if (!m_sound->IsPlayingMusic() && m_sound->IsCachedMusic("music/Intro1.ogg")) + if (!m_sound->IsPlayingMusic()) { m_sound->PlayMusic("music/Intro1.ogg", false); + m_sound->CacheMusic("music/Intro2.ogg"); } } From aa6345ab5e2a0303cf3349774889a710a6ba7258 Mon Sep 17 00:00:00 2001 From: krzys-h Date: Mon, 11 Jul 2016 09:29:47 +0200 Subject: [PATCH 02/56] Post-release 0.1.8-alpha --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c13a7208..771cc482 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,9 +16,9 @@ set(COLOBOT_VERSION_MINOR 1) set(COLOBOT_VERSION_REVISION 8) # Used on official releases -set(COLOBOT_VERSION_RELEASE_CODENAME "-alpha") +#set(COLOBOT_VERSION_RELEASE_CODENAME "-alpha") # Used on unreleased, development builds -#set(COLOBOT_VERSION_UNRELEASED "+alpha") +set(COLOBOT_VERSION_UNRELEASED "+alpha") # Append git characteristics to version if(DEFINED COLOBOT_VERSION_UNRELEASED) From 81b7bcc5bcac4778533f7753874bf83f6afc4254 Mon Sep 17 00:00:00 2001 From: krzys-h Date: Sun, 24 Jul 2016 14:38:49 +0200 Subject: [PATCH 03/56] Moved system modules from app/ to common/system/ --- src/CMakeLists.txt | 12 ++++++------ src/app/app.cpp | 3 ++- src/app/main.cpp | 9 +++++---- src/app/pathman.cpp | 10 ++++++---- src/app/signal_handlers.cpp | 4 ++-- src/common/config_file.cpp | 4 ++-- src/{app => common/system}/system.cpp | 10 +++++----- src/{app => common/system}/system.h | 2 +- src/{app => common/system}/system_linux.cpp | 2 +- src/{app => common/system}/system_linux.h | 4 ++-- src/{app => common/system}/system_macosx.cpp | 2 +- src/{app => common/system}/system_macosx.h | 6 +++--- src/{app => common/system}/system_other.cpp | 2 +- src/{app => common/system}/system_other.h | 4 ++-- src/{app => common/system}/system_windows.cpp | 2 +- src/{app => common/system}/system_windows.h | 4 ++-- src/graphics/engine/engine.cpp | 3 ++- test/unit/CMakeLists.txt | 4 ++-- test/unit/app/app_test.cpp | 4 ++-- test/unit/common/config_file_test.cpp | 4 ++-- .../{app => common/system}/system_linux_test.cpp | 4 ++-- .../{app => common/system}/system_windows_test.cpp | 4 ++-- 22 files changed, 54 insertions(+), 49 deletions(-) rename src/{app => common/system}/system.cpp (95%) rename src/{app => common/system}/system.h (99%) rename src/{app => common/system}/system_linux.cpp (98%) rename src/{app => common/system}/system_linux.h (95%) rename src/{app => common/system}/system_macosx.cpp (98%) rename src/{app => common/system}/system_macosx.h (92%) rename src/{app => common/system}/system_other.cpp (97%) rename src/{app => common/system}/system_other.h (95%) rename src/{app => common/system}/system_windows.cpp (99%) rename src/{app => common/system}/system_windows.h (95%) rename test/unit/{app => common/system}/system_linux_test.cpp (96%) rename test/unit/{app => common/system}/system_windows_test.cpp (96%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bc6f12ad..db4b9d95 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -89,8 +89,6 @@ endif() # Source files set(BASE_SOURCES ${OPENAL_SRC} - app/${SYSTEM_CPP_MODULE} - app/${SYSTEM_H_MODULE} app/app.cpp app/app.h app/controller.cpp @@ -103,10 +101,6 @@ set(BASE_SOURCES app/pausemanager.h app/signal_handlers.cpp app/signal_handlers.h - app/system.cpp - app/system.h - app/system_other.cpp - app/system_other.h common/config_file.cpp common/config_file.h common/error.h @@ -143,6 +137,12 @@ set(BASE_SOURCES common/resources/sndfile_wrapper.h common/restext.cpp common/restext.h + common/system/system.cpp + common/system/system.h + common/system/system_other.cpp + common/system/system_other.h + common/system/${SYSTEM_CPP_MODULE} + common/system/${SYSTEM_H_MODULE} common/settings.cpp common/settings.h common/singleton.h diff --git a/src/app/app.cpp b/src/app/app.cpp index d34532f7..d9583d09 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -22,7 +22,6 @@ #include "app/controller.h" #include "app/input.h" #include "app/pathman.h" -#include "app/system.h" #include "common/config_file.h" #include "common/image.h" @@ -34,6 +33,8 @@ #include "common/resources/resourcemanager.h" +#include "common/system/system.h" + #include "graphics/core/nulldevice.h" #include "graphics/opengl/glutil.h" diff --git a/src/app/main.cpp b/src/app/main.cpp index 88908f6f..abf65669 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -26,10 +26,6 @@ #include "app/app.h" #include "app/signal_handlers.h" -#include "app/system.h" -#if PLATFORM_WINDOWS - #include "app/system_windows.h" -#endif #include "common/logger.h" #include "common/make_unique.h" @@ -38,6 +34,11 @@ #include "common/resources/resourcemanager.h" +#include "common/system/system.h" +#if PLATFORM_WINDOWS + #include "common/system/system_windows.h" +#endif + #if PLATFORM_WINDOWS #include #endif diff --git a/src/app/pathman.cpp b/src/app/pathman.cpp index 8f862110..a3848280 100644 --- a/src/app/pathman.cpp +++ b/src/app/pathman.cpp @@ -23,15 +23,17 @@ #include "common/config.h" #include "app/app.h" -#include "app/system.h" -#ifdef PLATFORM_WINDOWS - #include "app/system_windows.h" -#endif + #include "common/logger.h" #include "common/resources/resourcemanager.h" +#include "common/system/system.h" +#ifdef PLATFORM_WINDOWS + #include "common/system/system_windows.h" +#endif + #include #include diff --git a/src/app/signal_handlers.cpp b/src/app/signal_handlers.cpp index 87dbb4ac..223d7db8 100644 --- a/src/app/signal_handlers.cpp +++ b/src/app/signal_handlers.cpp @@ -19,13 +19,13 @@ #include "app/signal_handlers.h" -#include "app/system.h" - #include "common/stringutils.h" #include "common/version.h" #include "common/resources/resourcemanager.h" +#include "common/system/system.h" + #include "level/robotmain.h" #include diff --git a/src/common/config_file.cpp b/src/common/config_file.cpp index 62862667..6148eaec 100644 --- a/src/common/config_file.cpp +++ b/src/common/config_file.cpp @@ -20,14 +20,14 @@ #include "common/config_file.h" -#include "app/system.h" - #include "common/logger.h" #include "common/make_unique.h" #include "common/resources/inputstream.h" #include "common/resources/outputstream.h" +#include "common/system/system.h" + #include #include #include diff --git a/src/app/system.cpp b/src/common/system/system.cpp similarity index 95% rename from src/app/system.cpp rename to src/common/system/system.cpp index fc55af24..210b4a75 100644 --- a/src/app/system.cpp +++ b/src/common/system/system.cpp @@ -18,18 +18,18 @@ */ -#include "app/system.h" +#include "common/system/system.h" #include "common/config.h" #if defined(PLATFORM_WINDOWS) - #include "app/system_windows.h" + #include "common/system/system_windows.h" #elif defined(PLATFORM_LINUX) - #include "app/system_linux.h" + #include "common/system/system_linux.h" #elif defined(PLATFORM_MACOSX) - #include "app/system_macosx.h" + #include "common/system/system_macosx.h" #else - #include "app/system_other.h" + #include "common/system/system_other.h" #endif #include "common/make_unique.h" diff --git a/src/app/system.h b/src/common/system/system.h similarity index 99% rename from src/app/system.h rename to src/common/system/system.h index ecb1de89..2be5ae16 100644 --- a/src/app/system.h +++ b/src/common/system/system.h @@ -18,7 +18,7 @@ */ /** - * \file app/system.h + * \file common/system/system.h * \brief System functions: time stamps, info dialogs, etc. */ diff --git a/src/app/system_linux.cpp b/src/common/system/system_linux.cpp similarity index 98% rename from src/app/system_linux.cpp rename to src/common/system/system_linux.cpp index 8cb59ecf..fd663919 100644 --- a/src/app/system_linux.cpp +++ b/src/common/system/system_linux.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see http://gnu.org/licenses */ -#include "app/system_linux.h" +#include "common/system/system_linux.h" #include "common/logger.h" diff --git a/src/app/system_linux.h b/src/common/system/system_linux.h similarity index 95% rename from src/app/system_linux.h rename to src/common/system/system_linux.h index af2fde64..e451a936 100644 --- a/src/app/system_linux.h +++ b/src/common/system/system_linux.h @@ -18,11 +18,11 @@ */ /** - * \file app/system_linux.h + * \file common/system/system_linux.h * \brief Linux-specific implementation of system functions */ -#include "app/system.h" +#include "common/system/system.h" #include diff --git a/src/app/system_macosx.cpp b/src/common/system/system_macosx.cpp similarity index 98% rename from src/app/system_macosx.cpp rename to src/common/system/system_macosx.cpp index 8b99a47a..47304876 100644 --- a/src/app/system_macosx.cpp +++ b/src/common/system/system_macosx.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see http://gnu.org/licenses */ -#include "app/system_macosx.h" +#include "common/system/system_macosx.h" #include "common/logger.h" diff --git a/src/app/system_macosx.h b/src/common/system/system_macosx.h similarity index 92% rename from src/app/system_macosx.h rename to src/common/system/system_macosx.h index 99873f05..c8abba53 100644 --- a/src/app/system_macosx.h +++ b/src/common/system/system_macosx.h @@ -18,12 +18,12 @@ */ /** - * \file app/system_macosx.h + * \file common/system/system_macosx.h * \brief MacOSX-specific implementation of system functions */ -#include "app/system.h" -#include "app/system_other.h" +#include "common/system/system.h" +#include "common/system/system_other.h" //@colobot-lint-exclude UndefinedFunctionRule diff --git a/src/app/system_other.cpp b/src/common/system/system_other.cpp similarity index 97% rename from src/app/system_other.cpp rename to src/common/system/system_other.cpp index 5b8d15f4..8547b829 100644 --- a/src/app/system_other.cpp +++ b/src/common/system/system_other.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see http://gnu.org/licenses */ -#include "app/system_other.h" +#include "common/system/system_other.h" void CSystemUtilsOther::Init() diff --git a/src/app/system_other.h b/src/common/system/system_other.h similarity index 95% rename from src/app/system_other.h rename to src/common/system/system_other.h index 37e61e6a..caac018d 100644 --- a/src/app/system_other.h +++ b/src/common/system/system_other.h @@ -18,11 +18,11 @@ */ /** - * \file app/system_other.h + * \file common/system/system_other.h * \brief Fallback code for other systems */ -#include "app/system.h" +#include "common/system/system.h" #include diff --git a/src/app/system_windows.cpp b/src/common/system/system_windows.cpp similarity index 99% rename from src/app/system_windows.cpp rename to src/common/system/system_windows.cpp index a9ae1544..4c6d13ef 100644 --- a/src/app/system_windows.cpp +++ b/src/common/system/system_windows.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see http://gnu.org/licenses */ -#include "app/system_windows.h" +#include "common/system/system_windows.h" #include "common/logger.h" diff --git a/src/app/system_windows.h b/src/common/system/system_windows.h similarity index 95% rename from src/app/system_windows.h rename to src/common/system/system_windows.h index 4379b3e6..dae9e0fa 100644 --- a/src/app/system_windows.h +++ b/src/common/system/system_windows.h @@ -18,11 +18,11 @@ */ /** - * \file app/system_windows.h + * \file common/system/system_windows.h * \brief Windows-specific implementation of system functions */ -#include "app/system.h" +#include "common/system/system.h" //@colobot-lint-exclude UndefinedFunctionRule diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp index 27d2db70..74a473ae 100644 --- a/src/graphics/engine/engine.cpp +++ b/src/graphics/engine/engine.cpp @@ -22,7 +22,6 @@ #include "app/app.h" #include "app/input.h" -#include "app/system.h" #include "common/image.h" #include "common/key.h" @@ -30,6 +29,8 @@ #include "common/make_unique.h" #include "common/stringutils.h" +#include "common/system/system.h" + #include "common/thread/resource_owning_thread.h" #include "graphics/core/device.h" diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 446834f7..c21fb600 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -1,8 +1,8 @@ # Platform-dependent tests if(PLATFORM_WINDOWS) - set(PLATFORM_TESTS app/system_windows_test.cpp) + set(PLATFORM_TESTS common/system/system_windows_test.cpp) elseif(PLATFORM_LINUX) - set(PLATFORM_TESTS app/system_linux_test.cpp) + set(PLATFORM_TESTS common/system/system_linux_test.cpp) endif() # Sources diff --git a/test/unit/app/app_test.cpp b/test/unit/app/app_test.cpp index 13a3caa9..073dd89a 100644 --- a/test/unit/app/app_test.cpp +++ b/test/unit/app/app_test.cpp @@ -19,10 +19,10 @@ #include "app/app.h" -#include "app/system_other.h" - #include "common/make_unique.h" +#include "common/system/system_other.h" + #include #include diff --git a/test/unit/common/config_file_test.cpp b/test/unit/common/config_file_test.cpp index b95b6b01..4a001373 100644 --- a/test/unit/common/config_file_test.cpp +++ b/test/unit/common/config_file_test.cpp @@ -17,11 +17,11 @@ * along with this program. If not, see http://gnu.org/licenses */ -#include "app/system.h" - #include "common/config_file.h" #include "common/logger.h" +#include "common/system/system.h" + #include #include #include diff --git a/test/unit/app/system_linux_test.cpp b/test/unit/common/system/system_linux_test.cpp similarity index 96% rename from test/unit/app/system_linux_test.cpp rename to test/unit/common/system/system_linux_test.cpp index d69b3fb5..9d51d884 100644 --- a/test/unit/app/system_linux_test.cpp +++ b/test/unit/common/system/system_linux_test.cpp @@ -17,8 +17,8 @@ * along with this program. If not, see http://gnu.org/licenses */ -#include "app/system.h" -#include "app/system_linux.h" +#include "common/system/system.h" +#include "common/system/system_linux.h" #include diff --git a/test/unit/app/system_windows_test.cpp b/test/unit/common/system/system_windows_test.cpp similarity index 96% rename from test/unit/app/system_windows_test.cpp rename to test/unit/common/system/system_windows_test.cpp index 46a729e4..1c2d8535 100644 --- a/test/unit/app/system_windows_test.cpp +++ b/test/unit/common/system/system_windows_test.cpp @@ -17,8 +17,8 @@ * along with this program. If not, see http://gnu.org/licenses */ -#include "app/system.h" -#include "app/system_windows.h" +#include "common/system/system.h" +#include "common/system/system_windows.h" #include From 5fea22ff03f3da5ee27be4039205010c203a8c23 Mon Sep 17 00:00:00 2001 From: krzys-h Date: Sun, 24 Jul 2016 16:18:25 +0200 Subject: [PATCH 04/56] Moved performance counters to a separate class --- src/CMakeLists.txt | 2 + src/app/app.cpp | 87 ++++++------------------------- src/app/app.h | 43 --------------- src/app/main.cpp | 3 ++ src/common/profiler.cpp | 88 +++++++++++++++++++++++++++++++ src/common/profiler.h | 78 ++++++++++++++++++++++++++++ src/graphics/engine/engine.cpp | 95 +++++++++++++++++----------------- 7 files changed, 235 insertions(+), 161 deletions(-) create mode 100644 src/common/profiler.cpp create mode 100644 src/common/profiler.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index db4b9d95..2cc25557 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -117,6 +117,8 @@ set(BASE_SOURCES common/logger.cpp common/logger.h common/make_unique.h + common/profiler.cpp + common/profiler.h common/regex_utils.cpp common/regex_utils.h common/resources/inputstream.cpp diff --git a/src/app/app.cpp b/src/app/app.cpp index d9583d09..3db0aaeb 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -28,6 +28,7 @@ #include "common/key.h" #include "common/logger.h" #include "common/make_unique.h" +#include "common/profiler.h" #include "common/stringutils.h" #include "common/version.h" @@ -113,9 +114,7 @@ CApplication::CApplication(CSystemUtils* systemUtils) m_private(MakeUnique()), m_configFile(MakeUnique()), m_input(MakeUnique()), - m_pathManager(MakeUnique(systemUtils)), - m_performanceCounters(), - m_performanceCountersData() + m_pathManager(MakeUnique(systemUtils)) { m_exitCode = 0; m_active = false; @@ -145,11 +144,6 @@ CApplication::CApplication(CSystemUtils* systemUtils) m_manualFrameLast = m_systemUtils->CreateTimeStamp(); m_manualFrameTime = m_systemUtils->CreateTimeStamp(); - for (int i = 0; i < PCNT_MAX; ++i) - { - m_performanceCounters[i][0] = m_systemUtils->CreateTimeStamp(); - m_performanceCounters[i][1] = m_systemUtils->CreateTimeStamp(); - } m_joystickEnabled = false; @@ -174,12 +168,6 @@ CApplication::~CApplication() m_systemUtils->DestroyTimeStamp(m_manualFrameLast); m_systemUtils->DestroyTimeStamp(m_manualFrameTime); - for (int i = 0; i < PCNT_MAX; ++i) - { - m_systemUtils->DestroyTimeStamp(m_performanceCounters[i][0]); - m_systemUtils->DestroyTimeStamp(m_performanceCounters[i][1]); - } - m_joystickEnabled = false; m_controller.reset(); @@ -995,12 +983,10 @@ int CApplication::Run() while (true) { - ResetPerformanceCounters(); - if (m_active) { - StartPerformanceCounter(PCNT_ALL); - StartPerformanceCounter(PCNT_EVENT_PROCESSING); + CProfiler::StartPerformanceCounter(PCNT_ALL); + CProfiler::StartPerformanceCounter(PCNT_EVENT_PROCESSING); } // To be sure no old event remains @@ -1089,9 +1075,9 @@ int CApplication::Run() m_controller->ProcessEvent(event); } - StopPerformanceCounter(PCNT_EVENT_PROCESSING); + CProfiler::StopPerformanceCounter(PCNT_EVENT_PROCESSING); - StartPerformanceCounter(PCNT_UPDATE_ALL); + CProfiler::StartPerformanceCounter(PCNT_UPDATE_ALL); // Prepare and process step simulation event Event event = CreateUpdateEvent(); @@ -1101,16 +1087,16 @@ int CApplication::Run() m_sound->FrameMove(m_relTime); - StartPerformanceCounter(PCNT_UPDATE_GAME); + CProfiler::StartPerformanceCounter(PCNT_UPDATE_GAME); m_controller->ProcessEvent(event); - StopPerformanceCounter(PCNT_UPDATE_GAME); + CProfiler::StopPerformanceCounter(PCNT_UPDATE_GAME); - StartPerformanceCounter(PCNT_UPDATE_ENGINE); + CProfiler::StartPerformanceCounter(PCNT_UPDATE_ENGINE); m_engine->FrameUpdate(); - StopPerformanceCounter(PCNT_UPDATE_ENGINE); + CProfiler::StopPerformanceCounter(PCNT_UPDATE_ENGINE); } - StopPerformanceCounter(PCNT_UPDATE_ALL); + CProfiler::StopPerformanceCounter(PCNT_UPDATE_ALL); /* Update mouse position explicitly right before rendering * because mouse events are usually way behind */ @@ -1118,9 +1104,7 @@ int CApplication::Run() Render(); - StopPerformanceCounter(PCNT_ALL); - - UpdatePerformanceCountersData(); + CProfiler::StopPerformanceCounter(PCNT_ALL); } } @@ -1405,14 +1389,14 @@ Event CApplication::CreateVirtualEvent(const Event& sourceEvent) /** Renders the frame and swaps buffers as necessary */ void CApplication::Render() { - StartPerformanceCounter(PCNT_RENDER_ALL); + CProfiler::StartPerformanceCounter(PCNT_RENDER_ALL); m_engine->Render(); - StopPerformanceCounter(PCNT_RENDER_ALL); + CProfiler::StopPerformanceCounter(PCNT_RENDER_ALL); - StartPerformanceCounter(PCNT_SWAP_BUFFERS); + CProfiler::StartPerformanceCounter(PCNT_SWAP_BUFFERS); if (m_deviceConfig.doubleBuf) SDL_GL_SwapWindow(m_private->window); - StopPerformanceCounter(PCNT_SWAP_BUFFERS); + CProfiler::StopPerformanceCounter(PCNT_SWAP_BUFFERS); } void CApplication::RenderIfNeeded(int updateRate) @@ -1849,45 +1833,6 @@ void CApplication::SetLanguage(Language language) GetLogger()->Debug("SetLanguage: Test gettext translation: '%s'\n", gettext("Colobot rules!")); } -void CApplication::StartPerformanceCounter(PerformanceCounter counter) -{ - m_systemUtils->GetCurrentTimeStamp(m_performanceCounters[counter][0]); -} - -void CApplication::StopPerformanceCounter(PerformanceCounter counter) -{ - m_systemUtils->GetCurrentTimeStamp(m_performanceCounters[counter][1]); -} - -float CApplication::GetPerformanceCounterData(PerformanceCounter counter) const -{ - return m_performanceCountersData[counter]; -} - -void CApplication::ResetPerformanceCounters() -{ - for (int i = 0; i < PCNT_MAX; ++i) - { - StartPerformanceCounter(static_cast(i)); - StopPerformanceCounter(static_cast(i)); - } -} - -void CApplication::UpdatePerformanceCountersData() -{ - long long sum = m_systemUtils->TimeStampExactDiff(m_performanceCounters[PCNT_ALL][0], - m_performanceCounters[PCNT_ALL][1]); - - for (int i = 0; i < PCNT_MAX; ++i) - { - long long diff = m_systemUtils->TimeStampExactDiff(m_performanceCounters[i][0], - m_performanceCounters[i][1]); - - m_performanceCountersData[static_cast(i)] = - static_cast(diff) / static_cast(sum); - } -} - bool CApplication::GetSceneTestMode() { return m_sceneTest; diff --git a/src/app/app.h b/src/app/app.h index 0edb5032..b6702907 100644 --- a/src/app/app.h +++ b/src/app/app.h @@ -94,34 +94,6 @@ enum MouseMode MOUSE_NONE, //! < no cursor visible }; -/** - * \enum PerformanceCounter - * \brief Type of counter testing performance - */ -enum PerformanceCounter -{ - PCNT_EVENT_PROCESSING, //! < event processing (except update events) - - PCNT_UPDATE_ALL, //! < the whole frame update process - PCNT_UPDATE_ENGINE, //! < frame update in CEngine - PCNT_UPDATE_PARTICLE, //! < frame update in CParticle - PCNT_UPDATE_GAME, //! < frame update in CRobotMain - - PCNT_RENDER_ALL, //! < the whole rendering process - PCNT_RENDER_PARTICLE, //! < rendering the particles in 3D - PCNT_RENDER_WATER, //! < rendering the water - PCNT_RENDER_TERRAIN, //! < rendering the terrain - PCNT_RENDER_OBJECTS, //! < rendering the 3D objects - PCNT_RENDER_INTERFACE, //! < rendering 2D interface - PCNT_RENDER_SHADOW_MAP, //! < rendering shadow map - - PCNT_SWAP_BUFFERS, //! < swapping buffers and vsync - - PCNT_ALL, //! < all counters together - - PCNT_MAX -}; - enum DebugMode { DEBUG_SYS_EVENTS = 1 << 0, @@ -292,13 +264,6 @@ public: void SetLanguage(Language language); //@} - //! Management of performance counters - //@{ - void StartPerformanceCounter(PerformanceCounter counter); - void StopPerformanceCounter(PerformanceCounter counter); - float GetPerformanceCounterData(PerformanceCounter counter) const; - //@} - bool GetSceneTestMode(); //! Renders the image in window @@ -333,11 +298,6 @@ protected: //! Internal procedure to reset time counters void InternalResumeSimulation(); - //! Resets all performance counters to zero - void ResetPerformanceCounters(); - //! Updates performance counters from gathered timer data - void UpdatePerformanceCountersData(); - protected: //! System utils instance CSystemUtils* m_systemUtils; @@ -382,9 +342,6 @@ protected: SystemTimeStamp* m_lastTimeStamp; SystemTimeStamp* m_curTimeStamp; - SystemTimeStamp* m_performanceCounters[PCNT_MAX][2]; - float m_performanceCountersData[PCNT_MAX]; - long long m_realAbsTimeBase; long long m_realAbsTime; long long m_realRelTime; diff --git a/src/app/main.cpp b/src/app/main.cpp index abf65669..389de93f 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -29,6 +29,7 @@ #include "common/logger.h" #include "common/make_unique.h" +#include "common/profiler.h" #include "common/restext.h" #include "common/version.h" @@ -99,6 +100,8 @@ int main(int argc, char *argv[]) auto systemUtils = CSystemUtils::Create(); // platform-specific utils systemUtils->Init(); + CProfiler::SetSystemUtils(systemUtils.get()); + // Add file output to the logger std::string logFileName; #if DEV_BUILD diff --git a/src/common/profiler.cpp b/src/common/profiler.cpp new file mode 100644 index 00000000..f94a92ec --- /dev/null +++ b/src/common/profiler.cpp @@ -0,0 +1,88 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * 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 3 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, see http://gnu.org/licenses + */ + +#include "common/profiler.h" + +#include "common/system/system.h" + +#include + +CSystemUtils* CProfiler::m_systemUtils = nullptr; +long long CProfiler::m_performanceCounters[PCNT_MAX] = {0}; +long long CProfiler::m_prevPerformanceCounters[PCNT_MAX] = {0}; +std::stack CProfiler::m_runningPerformanceCounters; +std::stack CProfiler::m_runningPerformanceCountersType; + +void CProfiler::SetSystemUtils(CSystemUtils* systemUtils) +{ + m_systemUtils = systemUtils; +} + +void CProfiler::StartPerformanceCounter(PerformanceCounter counter) +{ + if (counter == PCNT_ALL) + ResetPerformanceCounters(); + + SystemTimeStamp* timeStamp = m_systemUtils->CreateTimeStamp(); + m_systemUtils->GetCurrentTimeStamp(timeStamp); + m_runningPerformanceCounters.push(timeStamp); + m_runningPerformanceCountersType.push(counter); +} + +void CProfiler::StopPerformanceCounter(PerformanceCounter counter) +{ + assert(m_runningPerformanceCountersType.top() == counter); + m_runningPerformanceCountersType.pop(); + + SystemTimeStamp* timeStamp = m_systemUtils->CreateTimeStamp(); + m_systemUtils->GetCurrentTimeStamp(timeStamp); + m_performanceCounters[counter] += m_systemUtils->TimeStampExactDiff(m_runningPerformanceCounters.top(), timeStamp); + m_runningPerformanceCounters.pop(); + + if (counter == PCNT_ALL) + SavePerformanceCounters(); +} + +long long CProfiler::GetPerformanceCounterTime(PerformanceCounter counter) +{ + return m_prevPerformanceCounters[counter]; +} + +float CProfiler::GetPerformanceCounterFraction(PerformanceCounter counter) +{ + return static_cast(m_prevPerformanceCounters[counter]) / static_cast(m_prevPerformanceCounters[PCNT_ALL]); +} + +void CProfiler::ResetPerformanceCounters() +{ + for (int i = 0; i < PCNT_MAX; ++i) + { + m_performanceCounters[i] = 0; + } +} + +void CProfiler::SavePerformanceCounters() +{ + assert(m_runningPerformanceCounters.empty()); + + for (int i = 0; i < PCNT_MAX; ++i) + { + m_prevPerformanceCounters[i] = m_performanceCounters[i]; + } +} diff --git a/src/common/profiler.h b/src/common/profiler.h new file mode 100644 index 00000000..6eebc5f4 --- /dev/null +++ b/src/common/profiler.h @@ -0,0 +1,78 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * 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 3 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, see http://gnu.org/licenses + */ + +#pragma once + +class CSystemUtils; +struct SystemTimeStamp; + +#include + +/** + * \enum PerformanceCounter + * \brief Type of counter testing performance + */ +enum PerformanceCounter +{ + PCNT_EVENT_PROCESSING, //! < event processing (except update events) + + PCNT_UPDATE_ALL, //! < the whole frame update process + PCNT_UPDATE_ENGINE, //! < frame update in CEngine + PCNT_UPDATE_PARTICLE, //! < frame update in CParticle + PCNT_UPDATE_GAME, //! < frame update in CRobotMain + + PCNT_RENDER_ALL, //! < the whole rendering process + PCNT_RENDER_PARTICLE, //! < rendering the particles in 3D + PCNT_RENDER_WATER, //! < rendering the water + PCNT_RENDER_TERRAIN, //! < rendering the terrain + PCNT_RENDER_OBJECTS, //! < rendering the 3D objects + PCNT_RENDER_INTERFACE, //! < rendering 2D interface + PCNT_RENDER_SHADOW_MAP, //! < rendering shadow map + + PCNT_SWAP_BUFFERS, //! < swapping buffers and vsync + + PCNT_ALL, //! < all counters together + + PCNT_MAX +}; + +class CProfiler +{ +public: + static void SetSystemUtils(CSystemUtils* systemUtils); + + static void StartPerformanceCounter(PerformanceCounter counter); + static void StopPerformanceCounter(PerformanceCounter counter); + static long long GetPerformanceCounterTime(PerformanceCounter counter); + static float GetPerformanceCounterFraction(PerformanceCounter counter); + +private: + static void ResetPerformanceCounters(); + static void SavePerformanceCounters(); + +private: + static CSystemUtils* m_systemUtils; + + static long long m_performanceCounters[PCNT_MAX]; + static long long m_prevPerformanceCounters[PCNT_MAX]; + static std::stack m_runningPerformanceCounters; + static std::stack m_runningPerformanceCountersType; +}; + + diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp index 74a473ae..be1b7468 100644 --- a/src/graphics/engine/engine.cpp +++ b/src/graphics/engine/engine.cpp @@ -27,6 +27,7 @@ #include "common/key.h" #include "common/logger.h" #include "common/make_unique.h" +#include "common/profiler.h" #include "common/stringutils.h" #include "common/system/system.h" @@ -448,9 +449,9 @@ void CEngine::FrameUpdate() m_lightMan->UpdateProgression(rTime); - m_app->StartPerformanceCounter(PCNT_UPDATE_PARTICLE); + CProfiler::StartPerformanceCounter(PCNT_UPDATE_PARTICLE); m_particle->FrameParticle(rTime); - m_app->StopPerformanceCounter(PCNT_UPDATE_PARTICLE); + CProfiler::StopPerformanceCounter(PCNT_UPDATE_PARTICLE); ComputeDistance(); UpdateGeometry(); @@ -3220,9 +3221,9 @@ void CEngine::Render() } } - m_app->StartPerformanceCounter(PCNT_RENDER_INTERFACE); + CProfiler::StartPerformanceCounter(PCNT_RENDER_INTERFACE); DrawInterface(); - m_app->StopPerformanceCounter(PCNT_RENDER_INTERFACE); + CProfiler::StopPerformanceCounter(PCNT_RENDER_INTERFACE); // End the scene m_device->EndScene(); @@ -3261,7 +3262,7 @@ void CEngine::Draw3DScene() m_water->DrawBack(); // draws water background - m_app->StartPerformanceCounter(PCNT_RENDER_TERRAIN); + CProfiler::StartPerformanceCounter(PCNT_RENDER_TERRAIN); // Draw terrain @@ -3321,11 +3322,11 @@ void CEngine::Draw3DScene() if (!m_shadowMapping) DrawShadowSpots(); - m_app->StopPerformanceCounter(PCNT_RENDER_TERRAIN); + CProfiler::StopPerformanceCounter(PCNT_RENDER_TERRAIN); // Draw other objects - m_app->StartPerformanceCounter(PCNT_RENDER_OBJECTS); + CProfiler::StartPerformanceCounter(PCNT_RENDER_OBJECTS); bool transparent = false; @@ -3442,7 +3443,7 @@ void CEngine::Draw3DScene() } } - m_app->StopPerformanceCounter(PCNT_RENDER_OBJECTS); + CProfiler::StopPerformanceCounter(PCNT_RENDER_OBJECTS); m_lightMan->UpdateDeviceLights(ENG_OBJTYPE_TERRAIN); @@ -3455,9 +3456,9 @@ void CEngine::Draw3DScene() m_lightMan->DebugDumpLights(); } - m_app->StartPerformanceCounter(PCNT_RENDER_WATER); + CProfiler::StartPerformanceCounter(PCNT_RENDER_WATER); m_water->DrawSurf(); // draws water surface - m_app->StopPerformanceCounter(PCNT_RENDER_WATER); + CProfiler::StopPerformanceCounter(PCNT_RENDER_WATER); m_device->SetRenderState(RENDER_STATE_LIGHTING, false); @@ -3479,9 +3480,9 @@ void CEngine::Draw3DScene() } m_displayGoto.clear(); - m_app->StartPerformanceCounter(PCNT_RENDER_PARTICLE); + CProfiler::StartPerformanceCounter(PCNT_RENDER_PARTICLE); m_particle->DrawParticle(SH_WORLD); // draws the particles of the 3D world - m_app->StopPerformanceCounter(PCNT_RENDER_PARTICLE); + CProfiler::StopPerformanceCounter(PCNT_RENDER_PARTICLE); m_device->SetRenderState(RENDER_STATE_LIGHTING, true); @@ -3707,7 +3708,7 @@ void CEngine::RenderShadowMap() if (!m_shadowMapping) return; - m_app->StartPerformanceCounter(PCNT_RENDER_SHADOW_MAP); + CProfiler::StartPerformanceCounter(PCNT_RENDER_SHADOW_MAP); // If no shadow map texture exists, create it if (m_shadowMap.id == 0) @@ -3897,7 +3898,7 @@ void CEngine::RenderShadowMap() m_device->SetColorMask(true, true, true, true); m_device->Clear(); - m_app->StopPerformanceCounter(PCNT_RENDER_SHADOW_MAP); + CProfiler::StopPerformanceCounter(PCNT_RENDER_SHADOW_MAP); m_device->SetRenderMode(RENDER_MODE_NORMAL); m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, false); @@ -5030,7 +5031,7 @@ void CEngine::DrawStats() return; float height = m_text->GetAscent(FONT_COLOBOT, 13.0f); - float width = 0.25f; + float width = 0.4f; const int TOTAL_LINES = 20; Math::Point pos(0.05f * m_size.x/m_size.y, 0.05f + TOTAL_LINES * height); @@ -5053,56 +5054,56 @@ void CEngine::DrawStats() SetState(ENG_RSTATE_TEXT); - std::stringstream str; - - auto drawStatsLine = [&](const std::string& name, const std::string& value) + auto drawStatsLine = [&](const std::string& name = "", const std::string& value = "", const std::string& value2 = "") { if (!name.empty()) - { - str.str(""); - str << name << ": " << value; - m_text->DrawText(str.str(), FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f)); - } + m_text->DrawText(name+":", FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f)); + pos.x += 0.25f; + if (!value.empty()) + m_text->DrawText(value, FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f)); + pos.x += 0.15f; + if (!value2.empty()) + m_text->DrawText(value2, FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_RIGHT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f)); + pos.x -= 0.4f; pos.y -= height; }; - auto drawStatsValue = [&](const std::string& name, float value) + auto drawStatsValue = [&](const std::string& name, long long time) { - str.str(""); - str << std::fixed << std::setprecision(2) << value; - drawStatsLine(name, str.str()); + float value = static_cast(time)/CProfiler::GetPerformanceCounterTime(PCNT_ALL); + drawStatsLine(name, StrUtils::Format("%.2f", value), StrUtils::Format("%.2f ms", time/1e6f)); }; auto drawStatsCounter = [&](const std::string& name, PerformanceCounter counter) { - drawStatsValue(name, m_app->GetPerformanceCounterData(counter)); + drawStatsValue(name, CProfiler::GetPerformanceCounterTime(counter)); }; - float engineUpdate = m_app->GetPerformanceCounterData(PCNT_UPDATE_ENGINE) - - m_app->GetPerformanceCounterData(PCNT_UPDATE_PARTICLE); + long long engineUpdate = CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_ENGINE) - + CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_PARTICLE); - float otherUpdate = m_app->GetPerformanceCounterData(PCNT_UPDATE_ALL) - - engineUpdate - - m_app->GetPerformanceCounterData(PCNT_UPDATE_PARTICLE) - - m_app->GetPerformanceCounterData(PCNT_UPDATE_GAME); + long long otherUpdate = CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_ALL) - + engineUpdate - + CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_PARTICLE) - + CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_GAME); - float otherRender = m_app->GetPerformanceCounterData(PCNT_RENDER_ALL) - - m_app->GetPerformanceCounterData(PCNT_RENDER_PARTICLE) - - m_app->GetPerformanceCounterData(PCNT_RENDER_WATER) - - m_app->GetPerformanceCounterData(PCNT_RENDER_TERRAIN) - - m_app->GetPerformanceCounterData(PCNT_RENDER_OBJECTS) - - m_app->GetPerformanceCounterData(PCNT_RENDER_INTERFACE) - - m_app->GetPerformanceCounterData(PCNT_RENDER_SHADOW_MAP); + long long otherRender = CProfiler::GetPerformanceCounterTime(PCNT_RENDER_ALL) - + CProfiler::GetPerformanceCounterTime(PCNT_RENDER_PARTICLE) - + CProfiler::GetPerformanceCounterTime(PCNT_RENDER_WATER) - + CProfiler::GetPerformanceCounterTime(PCNT_RENDER_TERRAIN) - + CProfiler::GetPerformanceCounterTime(PCNT_RENDER_OBJECTS) - + CProfiler::GetPerformanceCounterTime(PCNT_RENDER_INTERFACE) - + CProfiler::GetPerformanceCounterTime(PCNT_RENDER_SHADOW_MAP); drawStatsCounter("Event processing", PCNT_EVENT_PROCESSING); - drawStatsLine("", ""); + drawStatsLine( ""); drawStatsCounter("Frame update", PCNT_UPDATE_ALL); drawStatsValue (" Engine update", engineUpdate); drawStatsCounter(" Particle update", PCNT_UPDATE_PARTICLE); drawStatsCounter(" Game update", PCNT_UPDATE_GAME); drawStatsValue( " Other update", otherUpdate); - drawStatsLine("", ""); + drawStatsLine( ""); drawStatsCounter("Frame render", PCNT_RENDER_ALL); drawStatsCounter(" Particle render", PCNT_RENDER_PARTICLE); drawStatsCounter(" Water render", PCNT_RENDER_WATER); @@ -5112,11 +5113,11 @@ void CEngine::DrawStats() drawStatsCounter(" Shadow map render", PCNT_RENDER_SHADOW_MAP); drawStatsValue( " Other render", otherRender); drawStatsCounter("Swap buffers & VSync", PCNT_SWAP_BUFFERS); - drawStatsLine("", ""); + drawStatsLine( ""); drawStatsLine( "Triangles", StrUtils::ToString(m_statisticTriangle)); - drawStatsValue( "FPS", m_fps); - drawStatsLine("", ""); - str.str(""); + drawStatsLine( "FPS", StrUtils::Format("%.3f", m_fps)); + drawStatsLine( ""); + std::stringstream str; str << std::fixed << std::setprecision(2) << m_statisticPos.x << "; " << m_statisticPos.z; drawStatsLine( "Position", str.str()); } From 2c3e90b126b8bcbb5f092a073bcb9d5822e2c146 Mon Sep 17 00:00:00 2001 From: krzys-h Date: Sun, 24 Jul 2016 16:36:13 +0200 Subject: [PATCH 05/56] Added CBot performance counter --- src/common/profiler.h | 1 + src/graphics/engine/engine.cpp | 11 +++++++---- src/object/implementation/programmable_impl.cpp | 3 +++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/common/profiler.h b/src/common/profiler.h index 6eebc5f4..9e222079 100644 --- a/src/common/profiler.h +++ b/src/common/profiler.h @@ -36,6 +36,7 @@ enum PerformanceCounter PCNT_UPDATE_ENGINE, //! < frame update in CEngine PCNT_UPDATE_PARTICLE, //! < frame update in CParticle PCNT_UPDATE_GAME, //! < frame update in CRobotMain + PCNT_UPDATE_CBOT, //! < running CBot code (part of CRobotMain update) PCNT_RENDER_ALL, //! < the whole rendering process PCNT_RENDER_PARTICLE, //! < rendering the particles in 3D diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp index be1b7468..36f08294 100644 --- a/src/graphics/engine/engine.cpp +++ b/src/graphics/engine/engine.cpp @@ -5032,7 +5032,7 @@ void CEngine::DrawStats() float height = m_text->GetAscent(FONT_COLOBOT, 13.0f); float width = 0.4f; - const int TOTAL_LINES = 20; + const int TOTAL_LINES = 21; Math::Point pos(0.05f * m_size.x/m_size.y, 0.05f + TOTAL_LINES * height); @@ -5083,9 +5083,11 @@ void CEngine::DrawStats() long long engineUpdate = CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_ENGINE) - CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_PARTICLE); + long long gameUpdate = CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_GAME) - + CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_CBOT); + long long otherUpdate = CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_ALL) - - engineUpdate - - CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_PARTICLE) - + CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_ENGINE) - CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_GAME); long long otherRender = CProfiler::GetPerformanceCounterTime(PCNT_RENDER_ALL) - @@ -5101,7 +5103,8 @@ void CEngine::DrawStats() drawStatsCounter("Frame update", PCNT_UPDATE_ALL); drawStatsValue (" Engine update", engineUpdate); drawStatsCounter(" Particle update", PCNT_UPDATE_PARTICLE); - drawStatsCounter(" Game update", PCNT_UPDATE_GAME); + drawStatsValue (" Game update", gameUpdate); + drawStatsCounter(" CBot programs", PCNT_UPDATE_CBOT); drawStatsValue( " Other update", otherUpdate); drawStatsLine( ""); drawStatsCounter("Frame render", PCNT_RENDER_ALL); diff --git a/src/object/implementation/programmable_impl.cpp b/src/object/implementation/programmable_impl.cpp index 0fb11bad..f02ba628 100644 --- a/src/object/implementation/programmable_impl.cpp +++ b/src/object/implementation/programmable_impl.cpp @@ -22,6 +22,7 @@ #include "CBot/CBot.h" #include "common/global.h" +#include "common/profiler.h" #include "level/robotmain.h" @@ -74,6 +75,7 @@ bool CProgrammableObjectImpl::EventProcess(const Event &event) if ( GetActivity() ) { + CProfiler::StartPerformanceCounter(PCNT_UPDATE_CBOT); if ( IsProgram() ) // current program? { if ( m_currentProgram->script->Continue() ) @@ -86,6 +88,7 @@ bool CProgrammableObjectImpl::EventProcess(const Event &event) { TraceRecordFrame(); } + CProfiler::StopPerformanceCounter(PCNT_UPDATE_CBOT); } } From fef050f6ddc1ada0c439c30374b2e44c55c4228a Mon Sep 17 00:00:00 2001 From: krzys-h Date: Sun, 24 Jul 2016 16:44:27 +0200 Subject: [PATCH 06/56] Fixed loading apperance scene, closes #802 --- src/level/robotmain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index 75c12f8a..7ed6ef21 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -2681,7 +2681,7 @@ void CRobotMain::ScenePerso() m_lightMan->FlushLights(); m_particle->FlushParticle(); - m_levelFile = "levels/other/perso000.txt"; + m_levelFile = "levels/other/perso.txt"; try { CreateScene(false, true, false); // sets scene From ffd688e2d72e210385830dced914e126f8bb90d4 Mon Sep 17 00:00:00 2001 From: krzys-h Date: Sun, 24 Jul 2016 16:56:47 +0200 Subject: [PATCH 07/56] Do not render the shadow map when pause blur is active --- src/graphics/engine/engine.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp index 36f08294..bbda7dcb 100644 --- a/src/graphics/engine/engine.cpp +++ b/src/graphics/engine/engine.cpp @@ -3187,9 +3187,6 @@ void CEngine::Render() color = m_backgroundColorDown; m_device->SetClearColor(color); - // Render shadow map - if (m_drawWorld && m_shadowMapping) - RenderShadowMap(); // Begin the scene m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, true); @@ -3203,6 +3200,10 @@ void CEngine::Render() } else { + // Render shadow map + if (m_drawWorld && m_shadowMapping) + RenderShadowMap(); + UseMSAA(true); DrawBackground(); // draws the background From 716218aa37e227ee97f34998c323b71b468e6ebf Mon Sep 17 00:00:00 2001 From: krzys-h Date: Sun, 24 Jul 2016 18:19:34 +0200 Subject: [PATCH 08/56] Fixed compile error on Windows --- src/common/resources/resourcemanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/resources/resourcemanager.cpp b/src/common/resources/resourcemanager.cpp index 3ed0dee6..86c77bfe 100644 --- a/src/common/resources/resourcemanager.cpp +++ b/src/common/resources/resourcemanager.cpp @@ -23,7 +23,7 @@ #include "common/config.h" #if PLATFORM_WINDOWS - #include "app/system_windows.h" + #include "common/system/system_windows.h" #endif #include "common/logger.h" From dbe7fd6ef074e3ce43cdd30ef357d73f279ffd5c Mon Sep 17 00:00:00 2001 From: krzys-h Date: Sun, 24 Jul 2016 18:19:37 +0200 Subject: [PATCH 09/56] Added performance counter for UI particles --- src/common/profiler.h | 3 ++- src/graphics/engine/engine.cpp | 14 +++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/common/profiler.h b/src/common/profiler.h index 9e222079..d0d373ab 100644 --- a/src/common/profiler.h +++ b/src/common/profiler.h @@ -39,7 +39,8 @@ enum PerformanceCounter PCNT_UPDATE_CBOT, //! < running CBot code (part of CRobotMain update) PCNT_RENDER_ALL, //! < the whole rendering process - PCNT_RENDER_PARTICLE, //! < rendering the particles in 3D + PCNT_RENDER_PARTICLE_WORLD, //! < rendering the particles in 3D + PCNT_RENDER_PARTICLE_IFACE, //! < rendering the particles in 2D interface PCNT_RENDER_WATER, //! < rendering the water PCNT_RENDER_TERRAIN, //! < rendering the terrain PCNT_RENDER_OBJECTS, //! < rendering the 3D objects diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp index bbda7dcb..7e5d4a80 100644 --- a/src/graphics/engine/engine.cpp +++ b/src/graphics/engine/engine.cpp @@ -3481,9 +3481,9 @@ void CEngine::Draw3DScene() } m_displayGoto.clear(); - CProfiler::StartPerformanceCounter(PCNT_RENDER_PARTICLE); + CProfiler::StartPerformanceCounter(PCNT_RENDER_PARTICLE_WORLD); m_particle->DrawParticle(SH_WORLD); // draws the particles of the 3D world - CProfiler::StopPerformanceCounter(PCNT_RENDER_PARTICLE); + CProfiler::StopPerformanceCounter(PCNT_RENDER_PARTICLE_WORLD); m_device->SetRenderState(RENDER_STATE_LIGHTING, true); @@ -4034,7 +4034,9 @@ void CEngine::DrawInterface() if (!m_screenshotMode && m_renderInterface) { + CProfiler::StartPerformanceCounter(PCNT_RENDER_PARTICLE_IFACE); m_particle->DrawParticle(SH_INTERFACE); // draws the particles of the interface + CProfiler::StopPerformanceCounter(PCNT_RENDER_PARTICLE_IFACE); } // 3D objects drawn in front of interface @@ -5033,7 +5035,7 @@ void CEngine::DrawStats() float height = m_text->GetAscent(FONT_COLOBOT, 13.0f); float width = 0.4f; - const int TOTAL_LINES = 21; + const int TOTAL_LINES = 22; Math::Point pos(0.05f * m_size.x/m_size.y, 0.05f + TOTAL_LINES * height); @@ -5080,6 +5082,7 @@ void CEngine::DrawStats() drawStatsValue(name, CProfiler::GetPerformanceCounterTime(counter)); }; + // TODO: Find a more generic way to calculate these in CProfiler long long engineUpdate = CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_ENGINE) - CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_PARTICLE); @@ -5092,7 +5095,7 @@ void CEngine::DrawStats() CProfiler::GetPerformanceCounterTime(PCNT_UPDATE_GAME); long long otherRender = CProfiler::GetPerformanceCounterTime(PCNT_RENDER_ALL) - - CProfiler::GetPerformanceCounterTime(PCNT_RENDER_PARTICLE) - + CProfiler::GetPerformanceCounterTime(PCNT_RENDER_PARTICLE_WORLD) - CProfiler::GetPerformanceCounterTime(PCNT_RENDER_WATER) - CProfiler::GetPerformanceCounterTime(PCNT_RENDER_TERRAIN) - CProfiler::GetPerformanceCounterTime(PCNT_RENDER_OBJECTS) - @@ -5109,11 +5112,12 @@ void CEngine::DrawStats() drawStatsValue( " Other update", otherUpdate); drawStatsLine( ""); drawStatsCounter("Frame render", PCNT_RENDER_ALL); - drawStatsCounter(" Particle render", PCNT_RENDER_PARTICLE); + drawStatsCounter(" Particle render", PCNT_RENDER_PARTICLE_WORLD); drawStatsCounter(" Water render", PCNT_RENDER_WATER); drawStatsCounter(" Terrain render", PCNT_RENDER_TERRAIN); drawStatsCounter(" Objects render", PCNT_RENDER_OBJECTS); drawStatsCounter(" UI render", PCNT_RENDER_INTERFACE); + drawStatsCounter(" particles", PCNT_RENDER_PARTICLE_IFACE); drawStatsCounter(" Shadow map render", PCNT_RENDER_SHADOW_MAP); drawStatsValue( " Other render", otherRender); drawStatsCounter("Swap buffers & VSync", PCNT_SWAP_BUFFERS); From 210b5c295d601ffea7acdb3a7505e0102571bc6f Mon Sep 17 00:00:00 2001 From: krzys-h Date: Sun, 24 Jul 2016 22:51:41 +0200 Subject: [PATCH 10/56] Fix memory leak related to performance counters This commit actually fixes two problems causing the leak: * DestroyTimeStamp not being called in CProfiler (my stupid mistake in 5fea22ff03f3da5ee27be4039205010c203a8c23) * DestroyTimeStamp leaving null pointers in CSystemUtils::m_timeStamps (this was introduced by @piotrdz long ago when introducing smart pointers) --- src/common/profiler.cpp | 2 ++ src/common/system/system.cpp | 7 ++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/common/profiler.cpp b/src/common/profiler.cpp index f94a92ec..f80ae849 100644 --- a/src/common/profiler.cpp +++ b/src/common/profiler.cpp @@ -53,6 +53,8 @@ void CProfiler::StopPerformanceCounter(PerformanceCounter counter) SystemTimeStamp* timeStamp = m_systemUtils->CreateTimeStamp(); m_systemUtils->GetCurrentTimeStamp(timeStamp); m_performanceCounters[counter] += m_systemUtils->TimeStampExactDiff(m_runningPerformanceCounters.top(), timeStamp); + m_systemUtils->DestroyTimeStamp(timeStamp); + m_systemUtils->DestroyTimeStamp(m_runningPerformanceCounters.top()); m_runningPerformanceCounters.pop(); if (counter == PCNT_ALL) diff --git a/src/common/system/system.cpp b/src/common/system/system.cpp index 210b4a75..ce594ea7 100644 --- a/src/common/system/system.cpp +++ b/src/common/system/system.cpp @@ -36,6 +36,7 @@ #include #include +#include std::unique_ptr CSystemUtils::Create() @@ -152,11 +153,7 @@ SystemTimeStamp* CSystemUtils::CreateTimeStamp() void CSystemUtils::DestroyTimeStamp(SystemTimeStamp *stamp) { - for (auto& timeStamp : m_timeStamps) - { - if (timeStamp.get() == stamp) - timeStamp.reset(); - } + m_timeStamps.erase(std::remove_if(m_timeStamps.begin(), m_timeStamps.end(), [&](const std::unique_ptr& timeStamp) { return timeStamp.get() == stamp; })); } void CSystemUtils::CopyTimeStamp(SystemTimeStamp *dst, SystemTimeStamp *src) From 48666c4604a5fbe897c7609767e4ee1ca4da5b35 Mon Sep 17 00:00:00 2001 From: melex750 Date: Wed, 3 Aug 2016 17:42:10 -0400 Subject: [PATCH 11/56] Fix not calling destructors after save/reload --- src/CBot/CBotStack.cpp | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/CBot/CBotStack.cpp b/src/CBot/CBotStack.cpp index 3170fb72..27c648f2 100644 --- a/src/CBot/CBotStack.cpp +++ b/src/CBot/CBotStack.cpp @@ -760,7 +760,17 @@ bool CBotVar::Save0State(FILE* pf) if (!WriteWord(pf, 100+static_cast(m_mPrivate)))return false; // private variable? if (!WriteWord(pf, m_bStatic))return false; // static variable? if (!WriteWord(pf, m_type.GetType()))return false; // saves the type (always non-zero) - if (!WriteWord(pf, static_cast(m_binit))) return false; // variable defined? + + if (m_type.Eq(CBotTypPointer) && GetPointer() != nullptr) + { + if (GetPointer()->m_bConstructor) // constructor was called? + { + if (!WriteWord(pf, (2000 + static_cast(m_binit)) )) return false; + return WriteString(pf, m_token->GetString()); // and variable name + } + } + + if (!WriteWord(pf, static_cast(m_binit))) return false; // variable defined? return WriteString(pf, m_token->GetString()); // and variable name } @@ -800,6 +810,13 @@ bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar) if ( w == CBotTypClass ) w = CBotTypIntrinsic; // necessarily intrinsic if (!ReadWord(pf, wi)) return false; // init ? + bool bConstructor = false; + if (w == CBotTypPointer && wi >= 2000) + { + wi -= 2000; + bConstructor = true; + } + CBotVar::InitType initType = static_cast(wi); if (!ReadString(pf, name)) return false; // variable name @@ -868,9 +885,10 @@ bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar) case CBotTypPointer: case CBotTypNullPointer: - if (!ReadString(pf, s)) return false; + if (!ReadString(pf, s)) return false; // name of the class { - pNew = CBotVar::Create(token, CBotTypResult(w, s));// creates a variable + CBotTypResult ptrType(w, s); + pNew = CBotVar::Create(token, ptrType);// creates a variable // CBotVarClass* p = nullptr; long id; ReadLong(pf, id); @@ -881,6 +899,8 @@ bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar) if ( !CBotVar::RestoreState( pf, pInstance ) ) return false; (static_cast(pNew))->SetPointer( pInstance ); // and point over + if (bConstructor) pNew->ConstructorSet(); // constructor was called + // if ( p != nullptr ) (static_cast(pNew))->SetPointer( p ); // rather this one } From 397e3124246a67ddb9b7f16b1fecdda6abd3f3cd Mon Sep 17 00:00:00 2001 From: melex750 Date: Wed, 3 Aug 2016 17:52:53 -0400 Subject: [PATCH 12/56] Fix RestoreState for subclass instance vars --- src/CBot/CBotStack.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/CBot/CBotStack.cpp b/src/CBot/CBotStack.cpp index 27c648f2..aa67264b 100644 --- a/src/CBot/CBotStack.cpp +++ b/src/CBot/CBotStack.cpp @@ -19,6 +19,8 @@ #include "CBot/CBotStack.h" +#include "CBot/CBotClass.h" + #include "CBot/CBotInstr/CBotFunction.h" #include "CBot/CBotVar/CBotVarPointer.h" @@ -866,10 +868,11 @@ bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar) if (isClass && p == nullptr) // set id for each item in this instance { CBotVar* pVars = pNew->GetItemList(); - long itemId = 1; - while (pVars != nullptr) + CBotVar* pv = pNew->GetClass()->GetVar(); + while (pVars != nullptr && pv != nullptr) { - pVars->m_ident = itemId++; + pVars->m_ident = pv->m_ident; + pv = pv->GetNext(); pVars = pVars->GetNext(); } } From d0a8a32a57ca3548258aba67753842b290168391 Mon Sep 17 00:00:00 2001 From: melex750 Date: Wed, 3 Aug 2016 18:34:39 -0400 Subject: [PATCH 13/56] Fix RestoreMethode when calling inherited methods --- src/CBot/CBotClass.cpp | 9 ++++++++- src/CBot/CBotInstr/CBotFunction.cpp | 14 +++++++++++--- src/CBot/CBotInstr/CBotFunction.h | 3 ++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/CBot/CBotClass.cpp b/src/CBot/CBotClass.cpp index 70c72ef5..17e16517 100644 --- a/src/CBot/CBotClass.cpp +++ b/src/CBot/CBotClass.cpp @@ -368,7 +368,14 @@ void CBotClass::RestoreMethode(long& nIdent, CBotVar** ppParams, CBotStack*& pStack) { - m_pMethod->RestoreCall(nIdent, name, pThis, ppParams, pStack, this); + CBotClass* pClass = this; + while (pClass != nullptr) + { + bool ok = pClass->m_pMethod->RestoreCall(nIdent, name, pThis, ppParams, pStack, pClass); + if (ok) return; + pClass = pClass->m_parent; + } + assert(false); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/CBot/CBotInstr/CBotFunction.cpp b/src/CBot/CBotInstr/CBotFunction.cpp index 4c15a416..d00e8ea6 100644 --- a/src/CBot/CBotInstr/CBotFunction.cpp +++ b/src/CBot/CBotInstr/CBotFunction.cpp @@ -801,7 +801,7 @@ int CBotFunction::DoCall(long& nIdent, const std::string& name, CBotVar* pThis, } //////////////////////////////////////////////////////////////////////////////// -void CBotFunction::RestoreCall(long& nIdent, const std::string& name, CBotVar* pThis, CBotVar** ppVars, +bool CBotFunction::RestoreCall(long& nIdent, const std::string& name, CBotVar* pThis, CBotVar** ppVars, CBotStack* pStack, CBotClass* pClass) { CBotTypResult type; @@ -810,14 +810,20 @@ void CBotFunction::RestoreCall(long& nIdent, const std::string& name, CBotVar* p if ( pt != nullptr ) { CBotStack* pStk = pStack->RestoreStack(pt); - if ( pStk == nullptr ) return; + if ( pStk == nullptr ) return true; pStk->SetProgram(pt->m_pProg); // it may have changed module CBotVar* pthis = pStk->FindVar("this"); pthis->SetUniqNum(-2); + if (pClass->GetParent() != nullptr) + { + CBotVar* psuper = pStk->FindVar("super"); + if (psuper != nullptr) psuper->SetUniqNum(-3); + } + CBotStack* pStk3 = pStk->RestoreStack(nullptr); // to set parameters passed - if ( pStk3 == nullptr ) return; + if ( pStk3 == nullptr ) return true; pt->m_param->RestoreState(pStk3, true); // parameters @@ -831,7 +837,9 @@ void CBotFunction::RestoreCall(long& nIdent, const std::string& name, CBotVar* p // finally calls the found function pt->m_block->RestoreState(pStk3, true); // interrupt ! + return true; } + return false; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/CBot/CBotInstr/CBotFunction.h b/src/CBot/CBotInstr/CBotFunction.h index b0bf501c..76a02bc0 100644 --- a/src/CBot/CBotInstr/CBotFunction.h +++ b/src/CBot/CBotInstr/CBotFunction.h @@ -179,8 +179,9 @@ public: * \param ppVars * \param pStack * \param pClass + * \return Returns true if the method call was restored. */ - void RestoreCall(long& nIdent, + bool RestoreCall(long& nIdent, const std::string& name, CBotVar* pThis, CBotVar** ppVars, From 860cdb0aea22bb797ea4718765c3056f05df83ed Mon Sep 17 00:00:00 2001 From: melex750 Date: Wed, 3 Aug 2016 19:13:21 -0400 Subject: [PATCH 14/56] Fix calling multi-level inherited methods --- src/CBot/CBotClass.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/CBot/CBotClass.cpp b/src/CBot/CBotClass.cpp index 17e16517..98a0a6f3 100644 --- a/src/CBot/CBotClass.cpp +++ b/src/CBot/CBotClass.cpp @@ -333,7 +333,7 @@ CBotTypResult CBotClass::CompileMethode(const std::string& name, r = m_pMethod->CompileCall(name, ppParams, nIdent); if ( r.Eq(CBotErrUndefCall) && m_parent != nullptr ) - return m_parent->m_pMethod->CompileCall(name, ppParams, nIdent); + return m_parent->CompileMethode(name, pThis, ppParams, pStack, nIdent); return r; } @@ -354,9 +354,7 @@ bool CBotClass::ExecuteMethode(long& nIdent, if (m_parent != nullptr) { - ret = m_parent->m_pCalls->DoCall(name, pThis, ppParams, pResult, pStack, pToken); - if (ret >= 0) return ret; - ret = m_parent->m_pMethod->DoCall(nIdent, name, pThis, ppParams, pStack, pToken, m_parent); + ret = m_parent->ExecuteMethode(nIdent, name, pThis, ppParams, pResult, pStack, pToken); } return ret; } From c9c02f5461d62193b90fce4a80e7f7e4e90c28f2 Mon Sep 17 00:00:00 2001 From: melex750 Date: Thu, 4 Aug 2016 01:06:37 -0400 Subject: [PATCH 15/56] Fix assigning instance to pointer for inheritance --- src/CBot/CBotDefParam.cpp | 5 +++++ src/CBot/CBotInstr/CBotDefClass.cpp | 4 +++- src/CBot/CBotInstr/CBotInstrMethode.cpp | 25 ++++++++++++++++++++++--- src/CBot/CBotInstr/CBotInstrMethode.h | 3 +++ src/CBot/CBotInstr/CBotLeftExpr.cpp | 7 +++++-- src/CBot/CBotInstr/CBotListArray.cpp | 7 +++++-- src/CBot/CBotStack.cpp | 1 + src/CBot/CBotVar/CBotVarPointer.cpp | 4 ++-- 8 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/CBot/CBotDefParam.cpp b/src/CBot/CBotDefParam.cpp index 5f164cab..d73c02e0 100644 --- a/src/CBot/CBotDefParam.cpp +++ b/src/CBot/CBotDefParam.cpp @@ -132,6 +132,11 @@ bool CBotDefParam::Execute(CBotVar** ppVars, CBotStack* &pj) (static_cast(newvar))->Copy(ppVars[i], false); break; case CBotTypPointer: + { + newvar->SetPointer(ppVars[i]->GetPointer()); + newvar->SetType(p->m_type); // keep pointer type + } + break; case CBotTypArrayPointer: { newvar->SetPointer(ppVars[i]->GetPointer()); diff --git a/src/CBot/CBotInstr/CBotDefClass.cpp b/src/CBot/CBotInstr/CBotDefClass.cpp index f7ea77a6..4c9ea4d4 100644 --- a/src/CBot/CBotInstr/CBotDefClass.cpp +++ b/src/CBot/CBotInstr/CBotDefClass.cpp @@ -174,7 +174,7 @@ CBotInstr* CBotDefClass::Compile(CBotToken* &p, CBotCStack* pStack, CBotClass* p CBotClass* result = pStk->GetClass(); if ( !pStk->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_POINTER).Eq(CBotTypNullPointer) && ( !pStk->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_POINTER).Eq(CBotTypPointer) || - ( result != nullptr && !pClass->IsChildOf(result) ))) // type compatible ? + ( result != nullptr && !result->IsChildOf(pClass) ))) // type compatible ? { pStk->SetError(CBotErrBadType1, p->GetStart()); goto error; @@ -282,7 +282,9 @@ bool CBotDefClass::Execute(CBotStack* &pj) { CBotVarClass* pInstance; pInstance = (static_cast(pile->GetVar()))->GetPointer(); // value for the assignment + CBotTypResult type = pThis->GetTypResult(); pThis->SetPointer(pInstance); + pThis->SetType(type); // keep pointer type } pThis->SetInit(CBotVar::InitType::DEF); } diff --git a/src/CBot/CBotInstr/CBotInstrMethode.cpp b/src/CBot/CBotInstr/CBotInstrMethode.cpp index 3bdf8492..207b9c12 100644 --- a/src/CBot/CBotInstr/CBotInstrMethode.cpp +++ b/src/CBot/CBotInstr/CBotInstrMethode.cpp @@ -63,6 +63,7 @@ CBotInstr* CBotInstrMethode::Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* if (pStack->IsOk()) { + inst->m_thisIdent = var->GetUniqNum(); CBotClass* pClass = var->GetClass(); // pointer to the class inst->m_className = pClass->GetName(); // name of the class CBotTypResult r = pClass->CompileMethode(inst->m_methodName, var, ppVars, @@ -144,8 +145,14 @@ bool CBotInstrMethode::ExecuteVar(CBotVar* &pVar, CBotStack* &pj, CBotToken* pre } ppVars[i] = nullptr; - CBotClass* pClass = CBotClass::Find(m_className); CBotVar* pThis = pile1->GetVar(); + CBotClass* pClass; + + if (m_thisIdent == -3) // super.method() + pClass = CBotClass::Find(m_className); + else + pClass = pThis->GetClass(); + CBotVar* pResult = nullptr; if (m_typRes.GetType() > 0) pResult = CBotVar::Create("", m_typRes); if (m_typRes.Eq(CBotTypClass)) @@ -204,7 +211,13 @@ void CBotInstrMethode::RestoreStateVar(CBotStack* &pile, bool bMain) } ppVars[i] = nullptr; - CBotClass* pClass = CBotClass::Find(m_className); + CBotClass* pClass; + + if (m_thisIdent == -3) // super.method() + pClass = CBotClass::Find(m_className); + else + pClass = pThis->GetClass(); + // CBotVar* pResult = nullptr; // CBotVar* pRes = pResult; @@ -253,8 +266,14 @@ bool CBotInstrMethode::Execute(CBotStack* &pj) } ppVars[i] = nullptr; - CBotClass* pClass = CBotClass::Find(m_className); CBotVar* pThis = pile1->GetVar(); + CBotClass* pClass; + + if (m_thisIdent == -3) // super.method() + pClass = CBotClass::Find(m_className); + else + pClass = pThis->GetClass(); + CBotVar* pResult = nullptr; if (m_typRes.GetType()>0) pResult = CBotVar::Create("", m_typRes); if (m_typRes.Eq(CBotTypClass)) diff --git a/src/CBot/CBotInstr/CBotInstrMethode.h b/src/CBot/CBotInstr/CBotInstrMethode.h index 6c8d1731..10bfe499 100644 --- a/src/CBot/CBotInstr/CBotInstrMethode.h +++ b/src/CBot/CBotInstr/CBotInstrMethode.h @@ -83,6 +83,9 @@ private: long m_MethodeIdent; //! Name of the class. std::string m_className; + //! Variable ID + long m_thisIdent; + }; } // namespace CBot diff --git a/src/CBot/CBotInstr/CBotLeftExpr.cpp b/src/CBot/CBotInstr/CBotLeftExpr.cpp index 881d841d..3a9fe155 100644 --- a/src/CBot/CBotInstr/CBotLeftExpr.cpp +++ b/src/CBot/CBotInstr/CBotLeftExpr.cpp @@ -182,15 +182,18 @@ bool CBotLeftExpr::Execute(CBotStack* &pj, CBotStack* array) if (t2.Eq(CBotTypPointer)) { CBotClass* c1 = t1.GetClass(); - CBotClass* c2 = t2.GetClass(); + CBotClass* c2 = var2->GetClass(); if ( !c2->IsChildOf(c1)) { CBotToken* pt = &m_token; pile->SetError(CBotErrBadType1, pt); return pj->Return(pile); // operation performed } + var1->SetVal(var2); // set pointer + var1->SetType(t1); // keep pointer type } - var1->SetVal(var2); // do assignment + else + var1->SetVal(var2); // do assignment } pile->SetCopyVar(var1); // replace the stack with the copy of the variable // (for name) diff --git a/src/CBot/CBotInstr/CBotListArray.cpp b/src/CBot/CBotInstr/CBotListArray.cpp index 89cbb79a..cf3bfddc 100644 --- a/src/CBot/CBotInstr/CBotListArray.cpp +++ b/src/CBot/CBotInstr/CBotListArray.cpp @@ -115,7 +115,7 @@ CBotInstr* CBotListArray::Compile(CBotToken* &p, CBotCStack* pStack, CBotTypResu goto error; } - CBotTypResult valType = pStk->GetTypResult(); + CBotTypResult valType = pStk->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); if (!TypeCompatible(valType, type, ID_ASS) ) { @@ -133,7 +133,7 @@ CBotInstr* CBotListArray::Compile(CBotToken* &p, CBotCStack* pStack, CBotTypResu goto error; } - CBotTypResult valType = pStk->GetTypResult(); + CBotTypResult valType = pStk->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); if (!TypeCompatible(valType, type, ID_ASS) ) { @@ -185,9 +185,12 @@ bool CBotListArray::Execute(CBotStack* &pj, CBotVar* pVar) pj->SetError(CBotErrOutArray, p->GetToken()); return false; } + CBotTypResult type = pVar2->GetTypResult(); if (!p->Execute(pile1, pVar2)) return false; // evaluate expression + if (type.Eq(CBotTypPointer)) pVar2->SetType(type); // keep pointer type + pile1->IncState(); } diff --git a/src/CBot/CBotStack.cpp b/src/CBot/CBotStack.cpp index aa67264b..295e6063 100644 --- a/src/CBot/CBotStack.cpp +++ b/src/CBot/CBotStack.cpp @@ -903,6 +903,7 @@ bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar) (static_cast(pNew))->SetPointer( pInstance ); // and point over if (bConstructor) pNew->ConstructorSet(); // constructor was called + if (ptrType.Eq(CBotTypPointer)) pNew->SetType(ptrType); // keep pointer type // if ( p != nullptr ) (static_cast(pNew))->SetPointer( p ); // rather this one diff --git a/src/CBot/CBotVar/CBotVarPointer.cpp b/src/CBot/CBotVar/CBotVarPointer.cpp index 059edb7e..f98b1463 100644 --- a/src/CBot/CBotVar/CBotVarPointer.cpp +++ b/src/CBot/CBotVar/CBotVarPointer.cpp @@ -174,9 +174,9 @@ CBotClass* CBotVarPointer::GetClass() //////////////////////////////////////////////////////////////////////////////// bool CBotVarPointer::Save1State(FILE* pf) { - if ( m_pClass ) + if ( m_type.GetClass() != nullptr ) { - if (!WriteString(pf, m_pClass->GetName())) return false; // name of the class + if (!WriteString(pf, m_type.GetClass()->GetName())) return false; // name of the class } else { From c89e6f4c28fc99bdec746ecc64aa8dc36536a6d5 Mon Sep 17 00:00:00 2001 From: melex750 Date: Thu, 4 Aug 2016 01:19:56 -0400 Subject: [PATCH 16/56] Begin adding unit tests for inheritance --- test/unit/CBot/CBot_test.cpp | 202 +++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index 2d96d59e..de07c2ab 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -1006,6 +1006,208 @@ TEST_F(CBotUT, ClassStringAdd_Issue535) ); } +TEST_F(CBotUT, ClassInheritanceVars) +{ + ExecuteTest( + "public class BaseClass {\n" + " int a = 123;\n" + " int b = 456;\n" + " int c = 789;\n" + "}\n" + "public class MidClass extends BaseClass {\n" + " int b = 1011;\n" + " int c = 1213;\n" + " int d = 1415;\n" + "}\n" + "public class SubClass extends MidClass {\n" + " int c = 1617;\n" + " int d = 1819;\n" + " int e = 2021;\n" + "}\n" + "extern void ClassInheritanceVars()\n" + "{\n" + " BaseClass bc();\n" + " ASSERT(bc.a == 123);\n" + " ASSERT(bc.b == 456);\n" + " ASSERT(bc.c == 789);\n" + " MidClass mc();\n" + " ASSERT(mc.a == 123);\n" + " ASSERT(mc.b == 1011);\n" + " ASSERT(mc.c == 1213);\n" + " ASSERT(mc.d == 1415);\n" + " SubClass sc();\n" + " ASSERT(sc.a == 123);\n" + " ASSERT(sc.b == 1011);\n" + " ASSERT(sc.c == 1617);\n" + " ASSERT(sc.d == 1819);\n" + " ASSERT(sc.e == 2021);\n" + // TODO Add tests for polymorphism here + "}\n" + ); +} + +TEST_F(CBotUT, ClassInheritanceMethods) +{ + ExecuteTest( + "public class BaseClass {\n" + " int a = 123;\n" + " int b = 456;\n" + " int c = 789;\n" + " int testOverride() { return 123; }\n" + " int testNoOverride() { return 456; }\n" + " int testInsideBaseClass() {\n" + " ASSERT(a == 123);\n" + " ASSERT(b == 456);\n" + " ASSERT(c == 789);\n" + " ASSERT(456 == testNoOverride());\n" + " return c;\n" + " }\n" + " int testInsideBaseOverride() { return testOverride(); }\n" + "}\n" + "public class MidClass extends BaseClass {\n" + " int b = 1011;\n" + " int c = 1213;\n" + " int d = 1415;\n" + " int testOverride() { return 1011; }\n" + " int testInsideMidClass() {\n" + " ASSERT(a == 123);\n" + " ASSERT(b == 1011);\n" + " ASSERT(c == 1213);\n" + " ASSERT(d == 1415);\n" + " ASSERT(456 == testNoOverride());\n" + " ASSERT(789 == testInsideBaseClass());\n" + " return c;\n" + " }\n" + " int testInsideMidOverride() { return testOverride(); }\n" + "}\n" + "public class SubClass extends MidClass {\n" + " int c = 1617;\n" + " int d = 1819;\n" + " int e = 2021;\n" + " int testOverride() { return 1617; }\n" + " int testInsideSubClass() {\n" + " ASSERT(a == 123);\n" + " ASSERT(b == 1011);\n" + " ASSERT(c == 1617);\n" + " ASSERT(d == 1819);\n" + " ASSERT(e == 2021);\n" + " ASSERT(456 == testNoOverride());\n" + " ASSERT(789 == testInsideBaseClass());\n" + " ASSERT(1213 == testInsideMidClass());\n" + " return c;\n" + " }\n" + " int testInsideSubOverride() { return testOverride(); }\n" + "}\n" + "extern void InheritanceMethods()\n" + "{\n" + " BaseClass bc();\n" + " ASSERT(123 == bc.testOverride());\n" + " ASSERT(456 == bc.testNoOverride());\n" + " ASSERT(789 == bc.testInsideBaseClass());\n" + " ASSERT(123 == bc.testInsideBaseOverride());\n" + " MidClass mc();\n" + " ASSERT(1011 == mc.testOverride());\n" + " ASSERT(456 == mc.testNoOverride());\n" + " ASSERT(789 == mc.testInsideBaseClass());\n" + " ASSERT(1213 == mc.testInsideMidClass());\n" + " ASSERT(1011 == mc.testInsideBaseOverride());\n" + " ASSERT(1011 == mc.testInsideMidOverride());\n" + " SubClass sc();\n" + " ASSERT(1617 == sc.testOverride());\n" + " ASSERT(456 == sc.testNoOverride());\n" + " ASSERT(789 == sc.testInsideBaseClass());\n" + " ASSERT(1213 == sc.testInsideMidClass());\n" + " ASSERT(1617 == sc.testInsideSubClass());\n" + " ASSERT(1617 == sc.testInsideBaseOverride());\n" + " ASSERT(1617 == sc.testInsideMidOverride());\n" + " ASSERT(1617 == sc.testInsideSubOverride());\n" + // TODO Add tests for polymorphism here + "}\n" + ); +} + +TEST_F(CBotUT, ClassInheritanceTestThis) +{ + ExecuteTest( + "public class BaseClass {\n" + " int a = 123;\n" + " int b = 456;\n" + " int c = 789;\n" + " void testBaseMembersAndParams(int a, int b, int c) {\n" + " ASSERT(a != 123);\n" + " ASSERT(b != 456);\n" + " ASSERT(c != 789);\n" + " ASSERT(this.a == 123);\n" + " ASSERT(this.b == 456);\n" + " ASSERT(this.c == 789);\n" + " }\n" + " BaseClass testReturnThisFromBaseClass() { return this; }\n" + "}\n" + "public class MidClass extends BaseClass {\n" + " int b = 1011;\n" + " int c = 1213;\n" + " int d = 1415;\n" + " void testMidMembersAndParams(int a, int b, int c, int d) {\n" + " ASSERT(a != 123);\n" + " ASSERT(b != 1011);\n" + " ASSERT(c != 1213);\n" + " ASSERT(d != 1415);\n" + " ASSERT(this.a == 123);\n" + " ASSERT(this.b == 1011);\n" + " ASSERT(this.c == 1213);\n" + " ASSERT(this.d == 1415);\n" + " }\n" + " MidClass testReturnThisFromMidClass() { return this; }\n" + "}\n" + "public class SubClass extends MidClass {\n" + " int c = 1617;\n" + " int d = 1819;\n" + " int e = 2021;\n" + " void testSubMembersAndParams(int a, int b, int c, int d, int e) {\n" + " ASSERT(a != 123);\n" + " ASSERT(b != 1011);\n" + " ASSERT(c != 1617);\n" + " ASSERT(d != 1819);\n" + " ASSERT(e != 2021);\n" + " ASSERT(this.a == 123);\n" + " ASSERT(this.b == 1011);\n" + " ASSERT(this.c == 1617);\n" + " ASSERT(this.d == 1819);\n" + " ASSERT(this.e == 2021);\n" + " }\n" + " SubClass testReturnThisFromSubClass() { return this; }\n" + "}\n" + "extern void ClassInheritanceTestThis()\n" + "{\n" + " BaseClass b();\n" + " b.testBaseMembersAndParams(-1, -2, -3);\n" + " BaseClass bc = b.testReturnThisFromBaseClass();\n" + " ASSERT(bc == b);\n" + " MidClass m();\n" + " m.testBaseMembersAndParams(-1, -2, -3);\n" + " m.testMidMembersAndParams(-1, -2, -3, -4);\n" + " MidClass mc = m.testReturnThisFromMidClass();\n" + " ASSERT(mc == m);\n" + " mc = m.testReturnThisFromBaseClass();\n" + " ASSERT(mc == m);\n" + " SubClass s();\n" + " s.testBaseMembersAndParams(-1, -2, -3);\n" + " s.testMidMembersAndParams(-1, -2, -3, -4);\n" + " s.testSubMembersAndParams(-1, -2, -3, -4, -5);\n" + " SubClass sc = s.testReturnThisFromSubClass();\n" + " ASSERT(sc == s);\n" + " sc = s.testReturnThisFromBaseClass();\n" + " ASSERT(sc == s);\n" + " sc = s.testReturnThisFromMidClass();\n" + " ASSERT(sc == s);\n" + // TODO Add tests for polymorphism here + "}\n" + ); +} + +// TODO TEST_F(CBotUT, ClassInheritanceTestSuper) + + TEST_F(CBotUT, String) { ExecuteTest( From fad38cd0e9313c6058f5ec7f7027f4904fdccbcc Mon Sep 17 00:00:00 2001 From: melex750 Date: Thu, 4 Aug 2016 03:16:59 -0400 Subject: [PATCH 17/56] Add accessing members to function calls --- src/CBot/CBotInstr/CBotExprRetVar.cpp | 183 ++++++++++++++++++++++++ src/CBot/CBotInstr/CBotExprRetVar.h | 45 ++++++ src/CBot/CBotInstr/CBotIndexExpr.h | 1 + src/CBot/CBotInstr/CBotInstrCall.cpp | 93 ++++++------ src/CBot/CBotInstr/CBotInstrCall.h | 4 + src/CBot/CBotInstr/CBotInstrMethode.cpp | 43 +++++- src/CBot/CBotInstr/CBotInstrMethode.h | 4 + src/CBot/CMakeLists.txt | 2 + 8 files changed, 330 insertions(+), 45 deletions(-) create mode 100644 src/CBot/CBotInstr/CBotExprRetVar.cpp create mode 100644 src/CBot/CBotInstr/CBotExprRetVar.h diff --git a/src/CBot/CBotInstr/CBotExprRetVar.cpp b/src/CBot/CBotInstr/CBotExprRetVar.cpp new file mode 100644 index 00000000..28b0fa83 --- /dev/null +++ b/src/CBot/CBotInstr/CBotExprRetVar.cpp @@ -0,0 +1,183 @@ + +#include +#include "CBot/CBotInstr/CBotExprRetVar.h" + +#include "CBot/CBotInstr/CBotExpression.h" +#include "CBot/CBotInstr/CBotInstrMethode.h" +#include "CBot/CBotInstr/CBotIndexExpr.h" +#include "CBot/CBotInstr/CBotFieldExpr.h" + +#include "CBot/CBotStack.h" + +namespace CBot +{ + +//////////////////////////////////////////////////////////////////////////////// +CBotExprRetVar::CBotExprRetVar() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +CBotExprRetVar::~CBotExprRetVar() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +CBotInstr* CBotExprRetVar::Compile(CBotToken*& p, CBotCStack* pStack) +{ + if (p->GetType() == ID_DOT) + { + CBotVar* var = pStack->GetVar(); + + if (var == nullptr) + { + pStack->SetError(CBotErrNoTerminator, p->GetStart()); + return nullptr; + } + + CBotCStack* pStk = pStack->TokenStack(); + CBotInstr* inst = new CBotExprRetVar(); + + while (true) + { + pStk->SetStartError(p->GetStart()); + if (var->GetType() == CBotTypArrayPointer) + { + if (IsOfType( p, ID_OPBRK )) + { + CBotIndexExpr* i = new CBotIndexExpr(); + i->m_expr = CBotExpression::Compile(p, pStk); + inst->AddNext3(i); + + var = var->GetItem(0,true); + + if (i->m_expr == nullptr || pStk->GetType() != CBotTypInt) + { + pStk->SetError(CBotErrBadIndex, p->GetStart()); + goto err; + } + if (!pStk->IsOk() || !IsOfType( p, ID_CLBRK )) + { + pStk->SetError(CBotErrCloseIndex, p->GetStart()); + goto err; + } + continue; + } + } + if (var->GetType(CBotVar::GetTypeMode::CLASS_AS_POINTER) == CBotTypPointer) + { + if (IsOfType(p, ID_DOT)) + { + CBotToken* pp = p; + + if (p->GetType() == TokenTypVar) + { + if (p->GetNext()->GetType() == ID_OPENPAR) + { + CBotInstr* i = CBotInstrMethode::Compile(p, pStk, var); + if (!pStk->IsOk()) goto err; + inst->AddNext3(i); + return pStack->Return(inst, pStk); + } + else + { + CBotFieldExpr* i = new CBotFieldExpr(); + i->SetToken(pp); + inst->AddNext3(i); + var = var->GetItem(p->GetString()); + if (var != nullptr) + { + i->SetUniqNum(var->GetUniqNum()); + if ( var->IsPrivate() && + !pStk->GetProgram()->m_bCompileClass) + { + pStk->SetError(CBotErrPrivate, pp); + goto err; + } + } + } + + if (var != nullptr) + { + p = p->GetNext(); + continue; + } + pStk->SetError(CBotErrUndefItem, p); + goto err; + } + pStk->SetError(CBotErrUndefClass, p); + goto err; + } + } + break; + } + + pStk->SetCopyVar(var); + if (pStk->IsOk()) return pStack->Return(inst, pStk); + + pStk->SetError(CBotErrUndefVar, p); +err: + delete inst; + return pStack->Return(nullptr, pStk); + } + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////// +bool CBotExprRetVar::Execute(CBotStack* &pj) +{ + + CBotStack* pile = pj->AddStack(); + CBotStack* pile1 = pile; + CBotVar* pVar; + + if (pile1->GetState() == 0) + { + pVar = pj->GetVar(); + pVar->Update(pj->GetUserPtr()); + + if ( !m_next3->ExecuteVar(pVar, pile, &m_token, true, false) ) + return false; + + if (pVar) + pile1->SetCopyVar(pVar); + else + return pj->Return(pile1); + + pile1->IncState(); + } + pVar = pile1->GetVar(); + + if (pVar == nullptr) + { + return pj->Return(pile1); + } + + if (pVar->IsUndefined()) + { + pile1->SetError(CBotErrNotInit, &m_token); + return pj->Return(pile1); + } + return pj->Return(pile1); +} + +//////////////////////////////////////////////////////////////////////////////// +void CBotExprRetVar::RestoreState(CBotStack* &pj, bool bMain) +{ + if (!bMain) return; + + CBotStack* pile = pj->RestoreStack(); + if ( pile == nullptr ) return; + + if (pile->GetState() == 0) + m_next3->RestoreStateVar(pile, bMain); +} + +std::string CBotExprRetVar::GetDebugData() +{ + std::stringstream ss; + ss << m_token.GetString() << "func(...).something" << std::endl; + return ss.str(); +} + +} // namespace CBot diff --git a/src/CBot/CBotInstr/CBotExprRetVar.h b/src/CBot/CBotInstr/CBotExprRetVar.h new file mode 100644 index 00000000..79c72ac6 --- /dev/null +++ b/src/CBot/CBotInstr/CBotExprRetVar.h @@ -0,0 +1,45 @@ + +#pragma once + +#include "CBot/CBotInstr/CBotInstr.h" + +namespace CBot +{ + +/** + * \brief Access a member/element of the variable on the stack + * + * + * + */ +class CBotExprRetVar : public CBotInstr +{ +public: + CBotExprRetVar(); + ~CBotExprRetVar(); + + static CBotInstr* Compile(CBotToken*& p, CBotCStack* pStack); + + /*! + * \brief Execute + * \param pj + * \return + */ + bool Execute(CBotStack* &pj) override; + + /*! + * \brief RestoreState + * \param pj + * \param bMain + */ + void RestoreState(CBotStack* &pj, bool bMain) override; + +protected: + virtual const std::string GetDebugName() override { return "CBotExprRetVar"; } + virtual std::string GetDebugData() override; + +private: + +}; + +} // namespace CBot diff --git a/src/CBot/CBotInstr/CBotIndexExpr.h b/src/CBot/CBotInstr/CBotIndexExpr.h index 15fe2d43..8ac6ed5c 100644 --- a/src/CBot/CBotInstr/CBotIndexExpr.h +++ b/src/CBot/CBotInstr/CBotIndexExpr.h @@ -69,6 +69,7 @@ private: CBotInstr* m_expr; friend class CBotLeftExpr; friend class CBotExprVar; + friend class CBotExprRetVar; }; } // namespace CBot diff --git a/src/CBot/CBotInstr/CBotInstrCall.cpp b/src/CBot/CBotInstr/CBotInstrCall.cpp index 111ec81c..6b45bb52 100644 --- a/src/CBot/CBotInstr/CBotInstrCall.cpp +++ b/src/CBot/CBotInstr/CBotInstrCall.cpp @@ -18,7 +18,8 @@ */ #include "CBot/CBotInstr/CBotInstrCall.h" -#include "CBot/CBotInstr/CBotExpression.h" +#include "CBot/CBotInstr/CBotExprRetVar.h" +#include "CBot/CBotInstr/CBotInstrUtils.h" #include "CBot/CBotStack.h" @@ -47,62 +48,26 @@ CBotInstrCall::~CBotInstrCall() //////////////////////////////////////////////////////////////////////////////// CBotInstr* CBotInstrCall::Compile(CBotToken* &p, CBotCStack* pStack) { - CBotVar* ppVars[1000]; - - int i = 0; CBotToken* pp = p; p = p->GetNext(); - pStack->SetStartError(p->GetStart()); - CBotCStack* pile = pStack; - - if ( IsOfType(p, ID_OPENPAR) ) + if (p->GetType() == ID_OPENPAR) { - int start, end; + + CBotVar* ppVars[1000]; + CBotInstrCall* inst = new CBotInstrCall(); inst->SetToken(pp); // compile la list of parameters - if (!IsOfType(p, ID_CLOSEPAR)) while (true) + inst->m_parameters = CompileParams(p, pStack, ppVars); + + if ( !pStack->IsOk() ) { - start = p->GetStart(); - pile = pile->TokenStack(); // keeps the results on the stack - - CBotInstr* param = CBotExpression::Compile(p, pile); - end = p->GetStart(); - if (inst->m_parameters == nullptr ) inst->m_parameters = param; - else inst->m_parameters->AddNext(param); // constructs the list - - if ( !pile->IsOk() ) - { - delete inst; - return pStack->Return(nullptr, pile); - } - - if ( param != nullptr ) - { - if ( pile->GetTypResult().Eq(99) ) - { - delete pStack->TokenStack(); - pStack->SetError(CBotErrVoid, p->GetStart()); - delete inst; - return nullptr; - } - ppVars[i] = pile->GetVar(); - ppVars[i]->GetToken()->SetPos(start, end); - i++; - - if (IsOfType(p, ID_COMMA)) continue; // skips the comma - if (IsOfType(p, ID_CLOSEPAR)) break; - } - - pStack->SetError(CBotErrClosePar, p->GetStart()); - delete pStack->TokenStack(); delete inst; return nullptr; } - ppVars[i] = nullptr; // the routine is known? // CBotClass* pClass = nullptr; @@ -124,6 +89,17 @@ CBotInstr* CBotInstrCall::Compile(CBotToken* &p, CBotCStack* pStack) } else pStack->SetVar(nullptr); // routine returns void + if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStack))) + { + inst->m_exprRetVar->SetToken(&inst->m_token); + delete pStack->TokenStack(); + } + if ( !pStack->IsOk() ) + { + delete inst; + return nullptr; + } + return inst; } p = pp; @@ -138,6 +114,17 @@ bool CBotInstrCall::Execute(CBotStack* &pj) CBotStack* pile = pj->AddStack(this); if ( pile->StackOver() ) return pj->Return( pile ); + CBotStack* pile3 = nullptr; + if (m_exprRetVar != nullptr) // func().member + { + pile3 = pile->AddStack2(); + if (pile3->GetState() == 1) // function call is done? + { + if (!m_exprRetVar->Execute(pile3)) return false; + return pj->Return(pile3); + } + } + // CBotStack* pile1 = pile; int i = 0; @@ -165,6 +152,14 @@ bool CBotInstrCall::Execute(CBotStack* &pj) if ( !pile2->ExecuteCall(m_nFuncIdent, GetToken(), ppVars, m_typRes)) return false; // interrupt + if (m_exprRetVar != nullptr) // func().member + { + pile3->SetCopyVar( pile2->GetVar() ); // copy the result + pile2->SetVar(nullptr); + pile3->SetState(1); // set call is done + return false; // go back to the top ^^^ + } + return pj->Return(pile2); // release the entire stack } @@ -176,6 +171,16 @@ void CBotInstrCall::RestoreState(CBotStack* &pj, bool bMain) CBotStack* pile = pj->RestoreStack(this); if ( pile == nullptr ) return; + if (m_exprRetVar != nullptr) // func().member + { + CBotStack* pile3 = pile->AddStack2(); + if (pile3->GetState() == 1) // function call is done? + { + m_exprRetVar->RestoreState(pile3, bMain); + return; + } + } + // CBotStack* pile1 = pile; int i = 0; diff --git a/src/CBot/CBotInstr/CBotInstrCall.h b/src/CBot/CBotInstr/CBotInstrCall.h index 7e8aba09..e39534f6 100644 --- a/src/CBot/CBotInstr/CBotInstrCall.h +++ b/src/CBot/CBotInstr/CBotInstrCall.h @@ -69,6 +69,10 @@ private: CBotTypResult m_typRes; //! Id of a function. long m_nFuncIdent; + + //! Instruction to return a member of the returned object. + CBotInstr* m_exprRetVar; + friend class CBotDebug; }; diff --git a/src/CBot/CBotInstr/CBotInstrMethode.cpp b/src/CBot/CBotInstr/CBotInstrMethode.cpp index 3bdf8492..78ed6b47 100644 --- a/src/CBot/CBotInstr/CBotInstrMethode.cpp +++ b/src/CBot/CBotInstr/CBotInstrMethode.cpp @@ -20,6 +20,7 @@ #include #include "CBot/CBotInstr/CBotInstrMethode.h" +#include "CBot/CBotInstr/CBotExprRetVar.h" #include "CBot/CBotInstr/CBotInstrUtils.h" #include "CBot/CBotStack.h" @@ -86,7 +87,16 @@ CBotInstr* CBotInstrMethode::Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* } pStack->SetVar(pResult); } - return inst; + else pStack->SetVar(nullptr); + + if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStack))) + { + inst->m_exprRetVar->SetToken(&inst->m_token); + delete pStack->TokenStack(); + } + + if ( pStack->IsOk() ) + return inst; } delete inst; return nullptr; @@ -106,6 +116,18 @@ bool CBotInstrMethode::ExecuteVar(CBotVar* &pVar, CBotStack* &pj, CBotToken* pre return pj->Return(pile1); } + CBotStack* pile3 = nullptr; + if (m_exprRetVar != nullptr) // .func().member + { + pile3 = pile1->AddStack2(); + if (pile3->GetState() == 1) + { + if (!m_exprRetVar->Execute(pile3)) return false; + pVar = nullptr; + return pj->Return(pile3); + } + } + if (pile1->IfStep()) return false; CBotStack* pile2 = pile1->AddStack(); // for the next parameters @@ -159,6 +181,15 @@ bool CBotInstrMethode::ExecuteVar(CBotVar* &pVar, CBotStack* &pj, CBotToken* pre pResult, pile2, GetToken())) return false; if (pRes != pResult) delete pRes; + if (m_exprRetVar != nullptr) // .func().member + { + pile3->SetCopyVar( pile2->GetVar() ); + pile2->SetVar(nullptr); + pile3->SetState(1); // set call is done + pVar = nullptr; + return false; // go back to the top ^^^ + } + pVar = nullptr; // does not return value for this return pj->Return(pile2); // release the entire stack } @@ -172,6 +203,16 @@ void CBotInstrMethode::RestoreStateVar(CBotStack* &pile, bool bMain) CBotStack* pile1 = pile->RestoreStack(this); // place for the copy of This if (pile1 == nullptr) return; + if (m_exprRetVar != nullptr) // .func().member + { + CBotStack* pile3 = pile1->AddStack2(); + if (pile3->GetState() == 1) // function call is done? + { + m_exprRetVar->RestoreState(pile3, bMain); + return; + } + } + CBotStack* pile2 = pile1->RestoreStack(); // and for the parameters coming if (pile2 == nullptr) return; diff --git a/src/CBot/CBotInstr/CBotInstrMethode.h b/src/CBot/CBotInstr/CBotInstrMethode.h index 6c8d1731..fb2b2058 100644 --- a/src/CBot/CBotInstr/CBotInstrMethode.h +++ b/src/CBot/CBotInstr/CBotInstrMethode.h @@ -83,6 +83,10 @@ private: long m_MethodeIdent; //! Name of the class. std::string m_className; + + //! Instruction to return a member of the returned object. + CBotInstr* m_exprRetVar; + }; } // namespace CBot diff --git a/src/CBot/CMakeLists.txt b/src/CBot/CMakeLists.txt index b77d0bbc..6ac4d130 100644 --- a/src/CBot/CMakeLists.txt +++ b/src/CBot/CMakeLists.txt @@ -54,6 +54,8 @@ set(SOURCES CBotInstr/CBotExprLitNum.h CBotInstr/CBotExprLitString.cpp CBotInstr/CBotExprLitString.h + CBotInstr/CBotExprRetVar.cpp + CBotInstr/CBotExprRetVar.h CBotInstr/CBotExprUnaire.cpp CBotInstr/CBotExprUnaire.h CBotInstr/CBotExprVar.cpp From c03d8beb8bc501f5c660ed843b1205d23484e6d4 Mon Sep 17 00:00:00 2001 From: krzys-h Date: Fri, 5 Aug 2016 16:55:10 +0200 Subject: [PATCH 18/56] Import documentation from old dev wiki I also updated all outdated information I could find, please tell me if I missed something Model format documentation needs an update --- CONTRIBUTING.md | 67 +++++++++++++- Doxyfile.in | 2 +- INSTALL-MSYS2.md | 161 +++++++++++++++++++++++++++++++++ INSTALL.md | 4 +- README-dev.md | 99 ++++++++++++++++++++ docimg/2d_coord.png | Bin 0 -> 4983 bytes docimg/3d_canonical_coords.png | Bin 0 -> 10961 bytes docimg/README.txt | 1 + src/app/main.cpp | 31 +++++-- src/graphics/README.txt | 45 +++++++++ src/graphics/model/README.txt | 82 +++++++++++++++++ 11 files changed, 475 insertions(+), 17 deletions(-) create mode 100644 INSTALL-MSYS2.md create mode 100644 README-dev.md create mode 100644 docimg/2d_coord.png create mode 100644 docimg/3d_canonical_coords.png create mode 100644 docimg/README.txt create mode 100644 src/graphics/model/README.txt diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c0b78712..030b439f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,9 +3,11 @@ So you want to contribute to Colobot: Gold Edition? That's awesome! Before you start, read this page, it contains a lot of useful information on how to do so. ## General information -Before you start, read more about technical details of the project. They can be found in our [Game Design Document](http://compiled.colobot.info/jenkins/job/colobot-gold-gdd/lastSuccessfulBuild/artifact/Colobot_Gold_Edition-Game_Design_Document.pdf) ([live editor](http://krzysh.pl:3000/project/545e90d99e8bceed2284797e)). +Before you start, read more about technical details of the project. They can be found in: -You may also find some useful information on the our (outdated) [dev wiki](http://colobot.info/wiki/dev). +* [Developer README](README-dev.md) +* [Doxygen documentation](http://compiled.colobot.info/job/colobot/job/colobot/job/dev/Doxygen/) +* [Game Design Document](http://compiled.colobot.info/job/colobot/job/colobot-misc/job/master/lastSuccessfulBuild/artifact/Colobot_Gold_Edition-Game_Design_Document.pdf) ## Before you start coding * If you want to fix a bug, please first check the related [issue on GitHub's bug tracker](https://github.com/colobot/colobot/issues). If there isn't one, make it. @@ -13,14 +15,69 @@ You may also find some useful information on the our (outdated) [dev wiki](http: * Before you start, check *"Assignee"* field in the issue and read the comments to see if nobody else is working on the same issue. If somebody is assigned to it, but there was no activity for a long time, you can take it over. Also, please post a comment on the issue that you want to help us, so other people don't waste time working at that issue in the same time. ## Coding style -See the [related page on dev wiki](http://colobot.info/wiki/dev/Coding_rules) for general guidelines, or [this file](https://github.com/colobot/colobot-lint/blob/master/RULES.md) for detailed description. +When writing code, please adhere to the following rules: -See [colobot-lint repository](https://github.com/colobot/colobot-lint) for automated tool that checks the coding style. +* Indent with spaces, 1 indentation level = 4 spaces. Unix line endings. And don't leave whitespace at the end of lines. Thank you. +* Put braces in new lines. + +Like that: +```c++ + if (a == b) + { + // ... + } + else + { + // ... + } +``` +NOT like that: +```c++ + if (a == b) { + // ... + } else { + // ... + } +``` +You may omit braces if there is only one line in block: +```c++ + if (a == b) + doStuff(); +``` +* Name functions beginning with upper case, e.g. `FooBar()` +* Name classes beginning with C, e.g. `class CSomeClass` +* Name accessors like so: `SetValue()` and `GetValue()` +* Name structs and enums beginning with uppercase letter, e.g. `struct SomeStruct` +* Enum values should begin with a prefix and underscore and should be all uppercase, e.g. `SOME_ENUM_VALUE` +* Use constant values instead of #define's, e.g. `const int MAX_SPACE = 1000;` instead of `#define MAX_SPACE 1000` (names should be all uppercase as in example) +* Don't use C-style casts, e.g. `(type)(foo)`. Use new C++-style casts, e.g. `static_cast(foo)`. +* Don't use global variables - use static class members whenever possible. +* Provide full namespace qualifier wherever possible, e.g. Math::MultiplyMatrices to avoid confusion. +* Document the new code in Doxygen. Even a short description is better than nothing at all. Also, if you are changing/rewriting old code, please do the same. +* Put comments in your code but not explaining its structure or what it does (Doxygen is for that), but **why** it does so. +* Whenever possible, please write unit tests for your code. Tests should go into `test/` subdirectory in each of the code directories. +* You can use STL classes where needed. +* Throwing exceptions is allowed, with the exception of CBot code (which has no automated memory management yet, so memory leaks could possibly occur) + +Also, when writing `#include`s: + +* first - in `.cpp` modules - associated `.h` header, e.g. `#include "app/app.h"` in `app.cpp` +* then - local includes, e.g. `#include "common/logger.h"` (listed in alphabetical order for clarity) +* and last - global includes, e.g. `#include `, `#include ` + +We also have an automated tool for checking the code style. See [colobot-lint repository](https://github.com/colobot/colobot-lint) for details. If your pull request breaks the coding style, you will have to fix it before it gets merged. +## Commiting rules +Please adhere to the following rules: +* Commits should have meaningful descriptions. +* Commits should not break the build nor tests. +* Changes in one commit should not be too extensive, unless necessary. +* Merges to *master* branch must be discussed beforehand and should include fully finished features if possible. + ## Submitting pull requests -After you finish working on your issue and want your code to be merged into the main repository, you should submit a **pull request**. Go to [this page](https://github.com/colobot/colobot/pulls) and select "New pull request". All pull request should ALWAYS be submitted to the *dev* branch. After your PR gets reviewed by our development team, it will be merged to *dev* branch, and on the next release - to the *master* branch. +After you finish working on your issue and want your code to be merged into the main repository, you should submit a **pull request**. Go to [this page](https://github.com/colobot/colobot/pulls) and select "New pull request". All pull requests should ALWAYS be submitted to the *dev* branch. After your PR gets reviewed by our development team, it will be merged to *dev* branch, and on the next release - to the *master* branch. If you need more help, see [GitHub's help page on Pull Requests](https://help.github.com/articles/using-pull-requests/). diff --git a/Doxyfile.in b/Doxyfile.in index c55e3284..c621ae33 100644 --- a/Doxyfile.in +++ b/Doxyfile.in @@ -859,7 +859,7 @@ EXAMPLE_RECURSIVE = NO # that contain images that are to be included in the documentation (see the # \image command). -IMAGE_PATH = +IMAGE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@/docimg" # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program diff --git a/INSTALL-MSYS2.md b/INSTALL-MSYS2.md new file mode 100644 index 00000000..f35634f3 --- /dev/null +++ b/INSTALL-MSYS2.md @@ -0,0 +1,161 @@ +This guide is written to help you with a process of compilation (the newest version of) Colobot: Gold Edition in a Windows environment. [MSYS2](http://sourceforge.net/projects/msys2/) will be used. + +Why MSYS2? +---------- + +You might ask, why you would use this method instead of the other one? Firstly, let's answer a question "why not just use MSYS?". + +The biggest problem with the compilation is **3rd party dependencies**. Installing compiler and tools like Git or Cmake is really nothing compared to trying to install endless number of libraries. Why is it that hard, you ask? Well, we, as the developers, would willingly help with this process for example by providing a package with all the needed stuff. Actually, there was a package once, but IT develops fast and new versions of software are released, including not only compiler, but also the libraries. This means that our package might work for compiler v.XX.1, but for v.XX.2 it might not. One of the programmers may use a new feature and Colobot may not compile with lib v.YY. Let's be honest -- it's too hard to provide new packages every time a new version of *something* comes up, at least for us, developers. You can check for yourself if current package works, it probably not. + +Here comes MSYS2. It provides everything that MSYS has and much more. It is basically a whole Linux-style development environment for Windows. Most importantly, it gives you a package manager, which makes not only installation of all the needed stuff easy, but also can take care of updating it with little human effort. + +Also, with the broken package, if you don't want to manually compile and install all the libraries, MSYS2 might be the only way to go. + +Guide +----- + +Compiling in Windows is harder than in most Linux distributions, but with MSYS2 it isn't impossible. + +### Setting Up an Environment + +Steps in this section needs to be done only once. After this, you can basically develop Colobot the same way you would do it normally (well, there might be a problem with integrating Windows-only IDEs). + +#### Installation of MSYS2 + +Read this page really carefully: . By following it, you should install and configure MSYS2 without major problems. + +#### TIP +When you face any problems during this guide or anywhere in the future, the first thing you should do is to close all MSYS2 processes and run `autorebase.bat` script (it's in the main MSYS2 folder), especially after installation and updating. If you don't do it, odd problems might appear, like CMake not detecting GCC. + +#### Running + +Now you need to install `MinGW-w64 toolchain`. Open `MSYS2 Shell` and enter the following command: + +```sh +pacman -S mingw-w64-i686-toolchain +``` + +**Warning:** Commands shown in this guide are for 32-bit operating system. If you have 64-bit OS, you must replace all `i686` occurrences with `x86_64`. + +MSYS2 creates a new environment (with all the "system" variables set properly) during this installation. You have done that from default `MSYS2 Shell`. To work with GOLD, you need to switch. There are two ways of "opening" an environment you can work in: + +1. Open MSYS2 Shell and enter + +```sh +export PATH=/mingw32/bin:$PATH +``` + +or + +```sh +export PATH=/mingw64/bin:$PATH +``` + +2. Open `MinGW-w64 Win32 Shell` (or `MinGW-w64 Win64 Shell`) + +You must do one of these two steps every time you want to compile GOLD. + +#### TIP +You can add "Open MSYS2 Shell here" to the context menu using registry. Save the following code as `.reg` file and run it. + +``` +Windows Registry Editor Version 5.00 +[HKEY_CLASSES_ROOT\Directory\Background\shell\open_msys2] +@="Open MSYS2 here" +[HKEY_CLASSES_ROOT\Directory\Background\shell\open_msys2\command] +@="c:\\msys32\\usr\\bin\\mintty.exe /bin/sh -lc 'cd \"$(cygpath \"%V\")\"; export PATH=\"/mingw32/bin:$PATH\"; exec bash'" +[HKEY_CLASSES_ROOT\Folder\shell\open_msys2] +@="Open MSYS2 here" +[HKEY_CLASSES_ROOT\Folder\shell\open_msys2\command] +@="c:\\msys32\\usr\\bin\\mintty.exe /bin/sh -lc 'cd \"$(cygpath \"%V\")\"; export PATH=\"/mingw32/bin:$PATH\"; exec bash'" +``` + +Remember to modify the paths before you use it so they fit in your installation. + +#### Installation of Tools and Dependencies + +To compile Colobot you need: + +- cmake +- git +- make +- gcc + +Install them: + +```sh +pacman -S msys2-devel git make mingw-w64-i686-cmake mingw-w64-i686-gcc +``` + +It's a good time to configure git or even ssh if you plan to push to the remote repository. + +When you are done, you can finally get the greatest benefit of using MSYS2. You are going to install required 3rd party libraries. This is how you do it: + +1. Find names of all required dependencies. They are listed in the [main INSTALL.md file](INSTALL.md#compiling-on-linux) + +2. Find each library using + +```sh +pacman -Ss dependency-name +``` + +3. Install each library using + +```sh +pacman -S package-name +``` + +Easy, isn't it? + +If you are lazy, you can just use this one-line command, although there is no guarantee it will work or install everything (might be out of date): + +```sh +pacman -S mingw-w64-i686-boost mingw-w64-i686-glew mingw-w64-i686-libpng gettext mingw-w64-i686-gettext mingw-w64-i686-libpng mingw-w64-i686-libsndfile mingw-w64-i686-libvorbis mingw-w64-i686-libogg mingw-w64-i686-openal mingw-w64-i686-SDL2 mingw-w64-i686-SDL2_image mingw-w64-i686-SDL2_ttf +``` + +You should now have everything set up and working. You can close all instances of MSYS2 and autorebase to ensure everything installed properly. + +### Compilation of GOLD + +You could say that you have a small Linux inside of your Windows right now. To compile GOLD just follow the instructions for Linux that are available in the repository (https://github.com/colobot/colobot/blob/dev/INSTALL.md\#compiling-on-linux). + +**Warning:** You must add `-G"MSYS Makefiles"` argument to `cmake`. For example, when you want to build a developer version: + +```sh +cmake -DCMAKE_BUILD_TYPE=Debug -DDEV_BUILD=1 -G"MSYS Makefiles" .. +``` + +### Dlls + +Your executable should run fine if you run it from the shell (like `./colobot` in a folder with a compiled binary). However, it will rather not run if you run it "from Windows", like by double clicking the shortcut. You will get error telling you that some dlls are missing. It's normal on Windows, so don't panic. Linker do dynamic linking by default, so binary must be distributed with the libraries stored in binary dll files. You can provide them in a few ways. + +#### PATH + +Add `C:\msys32\mingw32\bin` to your environment variable `PATH`. + +RMB on Computer -> Click Properties -> Advanced system settings -> Environment Variables -> Edit Path + +Be careful though. If you have any other installation of MinGW or other tools like FPC, git and so on, this might result in conflicts (for example two `gcc.exe` files) and the effects are unpredictable. + +You can use `PATH` also in other way: just copy all the dlls from `C:\msys32\mingw32\bin` to a place that's already in the `PATH`. This might result in a mess though, because you will mix files for GOLD with something else. + +#### Copy + +It's the way how it's done in most applications distributed for Windows. Just copy all the needed dlls to a folder with the game. All of them are in `C:\msys32\mingw32\bin`. + +You can simply try to run a game and each time it gives you an error copy a missing dll to folder with an executable (like `C:\Program Files\colobot`). Or, you can use this smart command (thanks @krzys-h!): + +```sh +ldd colobot.exe | grep -v /c/WINDOWS/ | cut -d ' ' -f 3 | while read -r line; do cp $line /c/path/to/the/game; done +``` + +#### Static Linking + +You can try to compile GOLD with static linking. Nobody have done that (at least in Windows) yet, but it's possible in theory. If you did it, please, let us know! + +The End +------- + +Hope, this guide helped you with using Windows as a development environment especially for Colobot. + +This method was first used and described by @MrSimbax. Script for gaining all the needed dlls was written by @krzys-h. \ No newline at end of file diff --git a/INSTALL.md b/INSTALL.md index 8b4265de..423166dc 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -40,7 +40,7 @@ statically linked Win32 binaries. More information is available in #### Compiling with MSYS2/MinGW-w64 -See [this wiki page](http://colobot.info/wiki/dev/Compiling_GOLD_on_Windows_with_MSYS2) for details. +See the [INSTALL-MSYS2.md](INSTALL-MSYS2.md) file for details. #### Compiling with MSVC @@ -125,7 +125,7 @@ So if you provided prefix "/some/prefix", you can run: ### Compiling on MacOS X -As of 0.1.2-alpha, we have added MacOS X support. See [INSTALL-MacOSX.md](https://github.com/colobot/colobot/blob/master/INSTALL-MacOSX.md) +As of 0.1.2-alpha, we have added MacOS X support. See [INSTALL-MacOSX.md](INSTALL-MacOSX.md) file for details. diff --git a/README-dev.md b/README-dev.md new file mode 100644 index 00000000..cb5f1fa1 --- /dev/null +++ b/README-dev.md @@ -0,0 +1,99 @@ +# README for developers +This file contains a ton of information useful for development of the game + +## Repository setup +All the repositories related to Colobot can be found on our GitHub organization page: https://github.com/colobot + +### Main code repository +This is the repository you are currently in. + +This repository contains all the source files of Colobot, along with some 3rd party libraries, testing framework and build files for CMake. + +### Data repository +The data repository is available at: https://github.com/colobot/colobot-data + +This repository contains the data files necessary to run the game. These are level files, textures, models, sounds, music, help files, translations, etc. It contains many binary files, and so, it may grow up to considerable size. If that becomes a problem, we may remove some old revisions of binary files to free up space. + +### Branch setup +Current setup is as follows: + +* branch **master** - will always contain the best-playable version of the project so that new users could download and compile the project without problems. This branch is not intended for actual development but an integration branch for more "public" releases. The changes should be pulled from general development branch - *dev*. This branch will also have tags at certain commits to denote releases. +* branch **dev** - development branch. This branch is used for general development. Changes committed here should be either pull requests implementing whole features, or incremental commits that do not change many files. + +Other **dev-*** branches may be created as needed, for work on major rewriting, or focusing on a set of features. + +## 3rd party libraries +3rd party libraries are bundled in `lib/`. They are provided for ease of use since the standard packages are rare in OS distributions. + +In case of GTest and GMock, CMake tries to detect if they are available in the system. If so, the system-provided versions will be used. + +## CMake build system +The build system is as follows: + +* `CMakeLists.txt` - definition of project, list of required packages, build type setup, general compiler options and reference to src subdirectory, +* `src/CMakeLists.txt` - defines the main colobot target, +* `src/CBot/CMakeLists.txt` - defines the CBot library target, +* `src/tools/CMakeLists.txt` - defines tool program targets, +* `data/CMakeLists.txt` - separate file in data submodule, includes routines for creating translations, manpage, icons, etc. and installing them with program. + +Test build system is discussed below. + +CMake sets some `#defines` which are passed to code in generated headers `common/config.h` and `common/version.h`. + +There are only a few include directories specified: + +* the main source directory `src/`, +* CMake binary directory (because of generated `common/config.h`), +* `lib/` directory with 3rd party libraries. + +Because of the above, include paths in source should always feature the full path from src, e.g. `#include "object/auto/auto.h"`, even if the file lies in the same directory. This will make it easier to move files around and change the `#include` lines with automated replace scripts (some of which are available in *tools/*). + +Note that the recommended way of building the project is to use separate build directory, where CMake will generate all targets. In this way, you can keep a clean source directory. The following shell commands illustrate this usage: + +```sh + cd + mkdir build + cmake ../ + make + bin/colobot -datadir ../data +``` + +## Tests organization +Tests are kept in `test/` directory which includes: + +* `test/cbot` - CBOT interpreter for test purposes, +* `test/unit` - automated unit tests. + +Each test directory contains own `CMakeLists.txt` specifying targets. Note however that the only targets added as automated tests in CMake are in `test/unit` directory. The other targets are used as development support tools, not automated tests. + +Tests can be enabled or disabled using CMake option TESTS (OFF by default). To run the automated tests (you must be in the build directory): +``` + ./colobot_ut + # or: + make test + # or: + ctest -V . +``` + +For unit testing, we use Google Test framework (http://code.google.com/p/googletest/) and for mocking objects and function calls, GMock library (http://code.google.com/p/gmock/). + +Note that currently, there are very few well-tested parts of code. It is quite impossible to write unit tests for all the code we have but we should test as much code as possible, if not at unit level, then at module level. Hence the test environments and code snippets that enable us to test parts of code without actually running the full-fledged game. + +## CI and automated builds +Our team is not large, but since compiling the whole project is lengthy and requires some knowledge, we use some automated services to facilitate the development. + +Currently we use Jenkins server hosted at http://compiled.colobot.info/ to run tests and provide compiled game binaries. + +Testers are encouraged to use these packages to test the game and report any problems. + +## Code documentation +[Doxygen](http://www.stack.nl/~dimitri/doxygen/) is used as the documentation tool. + +The documentation is extracted from comment blocks in code by running `make doc`. The resulting HTML files are available in ''doc/'' inside the build directory. Generated documentation files '''will not''' be included in the repository. The idea is that anyone can generate the documentation at any time, in his local copy. Our Jenkins also builds documentation after every commit, see http://compiled.colobot.info/job/colobot/job/colobot/job/dev/Doxygen/ + +Diagram generation (class inheritance, include dependencies, etc.) is disabled for now to speed up the generation process but you can of course enable it in your local copy. + +Currently, only a few classes and structs are documented properly. If you can, please expand the documentation wherever possible. + +## Coding rules +Please refer to the [CONTRIBUTING.md](CONTRIBUTING.md#coding-style) file. diff --git a/docimg/2d_coord.png b/docimg/2d_coord.png new file mode 100644 index 0000000000000000000000000000000000000000..bd4676bee184529aa6487047fc117d55aff4e933 GIT binary patch literal 4983 zcmeHL`8(9z-=9V3wtf;dc4K0sY$M7Vjb#!-A-A0wM3!tL+f0;CNu-e`TScUdCHp?z zwmXJOmdRK~-A0A%Tf%pq=RbJv>-pjN;W^iJ_V+p0b5px$NVrud($Mh^O z!qM_cXcNKbplF73rN72FmS$DnSvQ}bu2DNnKiDOa;#_+Ek$>1- zvKD`qp2ksmlQ4O-Jn(>Ouq_F?Q|<7TV!?9P?B^5%hrlK9{^1B>&~A|bv? znJC(D)BzOkQxaJo2NUGM+2iFcGa+!3%u5LVOcecW2I?~JP1HdRQ4}uge{4bqnHosp zz-HOZp}J=xJZv;O38?a7i9aaPF(cHurHl-THi$cmZ21#pS=X~rrKdV3O;6uF#0v?z z2MUdq-9yy|_`{#o3Ew}UXI*5ChAQCBaR;D8fl8I7oL@H(p=C54T*p;3Tmh*An-n(S zarH=5hY%nk!LQvjhl-yG@;KHAxABZ$?i={a@n(^NP6YXbzfA5>?e}bYHo2dqSkgww z94db%z;k|%T}3~FS-ss|_Yh4>j%J?E&lw5eO~$1!CatI(1ai|eAe_GE!% zkZFjV6a!R;xs?R4lYV{cBW2|6ksCF_dQE>GOn*X5I~JfJ=qeA1zGzglIj43H%p<46 zL^kx$$dLp0y^=>vEC_qjTYoH_W()0>zCl?h&egb)t9luM$S+8CDEm*hoJpQ>{2d*0~Ml}z5h=m$*K53&V-Dl@{Qt3Js?C^xt8!~lir1a+eQ z_@`Fg%WLYYDh}=`8iIhv2{q)2*GC9HE5ofdj$B3I;KA~61pUu53KrL~$X5DgyI^^_XGreU*$qb@GYIR{*eu*g$Oh8B_Icaya+`njK9E zS?$AfK104XzIaI8cTArM5O2;u&>}+A44rhY_+$(JYzl%_WDmF%0zXoPq7@sGRap(| zxzu`S`^!lG;j_Fjy!G=ut?(4aZwFS^1MMXOT#*){BOd(Uvy)oA47pl2K3w2!kV$zH zbtQ*f2@Xzucx7@;!P34x!tqpFeV5+&r_1RhNBJ{vPlTY;2Pf*f^nwIjSzp$naFfA| z*D~)$Vbv~W-6x7oj$!7Y>FMhBkbrq5Ls8@xA^HOKOod&WaHZH%55v`VW=j1thZZ8(KR~7mCUNBQo(p>-ZM5Qr0XPS^&0th8Pnu=kclhbZ zc_i)&5^p$dzedONdq_tw?D*P;Aj~>BbOb%qe49*{BlVG_kltR{poy=C`&VTS1FI^w z*3AdD;=&6UuIAf4XWAOAGjEjIo;OI>Dr#;1ZAe8JCOk?p!3JH}ChdoQWoD;^21Ose zSP|nw*o(P7Iq@TB-YD6JR6yW}=iLN}$Co16eUw=#;|GJy`xEZ(alu5`G1p0p7`-t| z8@TWtP`ScfruCWr08{5bVJb`=xoKt!I=h>!BQ3nQH^ zAd_cHe5sg`%etI!d`Zu%hLECyub;s*FOaJxl)hKryHQ znrj)e#?hlFm*!K}ZMQhyXKI{C>m-nv=z1QDxA4?Jtc4WrU{*$@3xuW&O*|Am^839sJG}`q7 z0&y&$QilKc1V4*oi#Ox#;x0n6&lvV*UsMXzz9#I-?3pued@U>TkUDD7d4RsxZ%yYz zKfHtzs*`gFmZD9eJp{`}*jZi}s*LI=jJ5WyWm`d`Was-?ph{7rFnBKe3SG-{nxExy zj*4g~Yjfvsk?lC^;xG>VEu`uK??ZP?csS3hmU?Nyr>}atv2kfDwsyf^rG@(ZB-#tK z>=wocYyeGVUMLtN))vGZ$?2xmRtAViliooefi!K-_mW@;w=X&AJ5~ZjQD;FEFpWZ zi*x;9YTni^f_G;ADb+J>gHWhBZUOi6<>dL4mZ5~IaYD(A7G0$9(|ePMxhhu0jf$*c zmYG~(yW%5MK$`A%fo41>U3o_`ZSEwTL%VK)8|-MvPx-fa{=dTt{(qN$O{o6wUER=g zkQju`l>=hy+>UT*ClsoIQ#ttSR8^0dn^WoD|+udnp@MLZeJuMKpzXW@HRl1 zsfPro(!p8l%WgOgqKswGBszvISEXM(Zkk)6;8m~ zrcweeua15qyIC+d5bP%^Wqm}Z>Eq+TvJ89MP-o#0HQOdK9<}a-Wk3>y%A8U&SB@=Z z({(l!%7XkY5O#ZPKbXkKs1uY=ig)md0wx5$0abgN{1#3DEOu;rG(fOc@yo^Ys%&Gl zmxdfu5wm~eO$X$5!>tbhFzcXnYd)819MEi(`AZ#1fNkPAa%1II4%Yx_n6yZcQ)(h4 z_c8T|HQvo7KmprX%>9?JLE}EGdmzyU8>D@I)kqVWFAk`#W3Zh+fv}+T4%vA-r2wH! zs#USi(Mvs$f|BI7Fwggl@z_@#kg;Zbg!U4H(NgUo3e1^zHqvR72vC{Dxqr0S zPINOm_v>@^sh1)@RQ&QiK87!INX`#F#l!LCxL@Nj>7nqM?sE1e*frh74qW^*Bny#7 z;roXHi}LP({ZuWnJ7+za0uCh=R{ItJP7#^SH70Us+|COpQ5+p zszK(EEl?GSW!wSRC1+SeD;9N^>mSqr{OV9O1|y}ZH>$Aa4>kY!SZE~|Y|r#==TFs! ztTN-91+-RE=!W#vZkGU@WFekD`mIUIUcX-bfN&pUhxLkSImVy-nbMWh)L+(~$U^U1 z@3wZKXIZcYeazywu=(5E_@^+=j+RsupoY}(Pw2@1XO1QV+pG@qx^$4PD!4cZeo^- z-n)9`^!+vk+_J>R=_75@L5_>X98A}|(?~3lFN3;@-YqQPH9BZ;gor0h z4yUhfvM$w&>P@XBPN5g$h`K3HqTF?pp0i{O@6Q?4Atc)U)+ADjzE7vo-6+6mnI6bK zyyXb-Wax(<`6rP1=NsLsMey2Rf_e{qnn<$!$!jFg68k)BamA*0{#!fWq-Nf%zICMh ziC69QS1D7jiN5cDtdIl5rO7rIwdXdrZUlxk%k2c(Sa|MWjI=DEUhpkO*-9#} z*Qwh3hE8SNeY!5GFi_n9SQcc@(EXY$P-dN_6JjG;;m}Ia(?-aViXG_-SNL`vCPwdo zk!W0exKgLO!i=Us3SJW z@R!$uzoO^>@5Vw^P}c*>9!W~^O9@?^g6NYAny*)%sUl9en{4Y;p^)XzE?m-o_$K3W zeKuqhHfytHZTaMqt1JFrfZ3|vn zZ#2oRd+gbzT?A7Z6N2ce$}AqKEi>5iMJ8AyW~$E>wqTUM7n^U_(c>v0+vf_ixREZO zR`{f|GM8WN){1N92f7zfv+vD1;~aubxtNh-(djWlM)`6U=+P>HRN6%CoGRI)as}+X zLXS*47Bxcb_}!_E`!srsUflojbfi~<$nb;Pf5Q7bEIJ=HN5zXR`N+1}zkNp;_|XU~ z4}x1qXCzi|hWStHtY{q=eq!lnGg(d6z^92LdG8J-WhK zL@8e zLNeTG6kXybs#J?B{gAlQ&nvDvbjXvd4jt#hJP$XVE$Zc}Lr|_d^sh`_?ele?AHd$4 TmJH&GWDuO04Yu6W^X7j5%nHR# literal 0 HcmV?d00001 diff --git a/docimg/3d_canonical_coords.png b/docimg/3d_canonical_coords.png new file mode 100644 index 0000000000000000000000000000000000000000..d9b3ace25d5b24717b45cb96065fa66698845d50 GIT binary patch literal 10961 zcmdsdc|6qZ`|m7_WNTEEwFX7DGKho}X((&TzRze7Q}%2#t@ETPO*CcAmTZH@zI=M} zsK#h|kfp*@56Xy<7`t^Cl95WyMClqlUcXkW-m#`&}3Vs(QopOtS!KC~)f8fu|ONfBD zJJQNE(m9+E8RK^u4~vP3(IJL~LVM+X4n0QYd)v9Ka_r|ek(OlPfg{_b<1x?-?m{Q zeUIQovd!AfD`%psq6LXFi8HPJZI#S^-coG&>k(da8?V`HrHz*~!&~;g_2#rH&Z$|Z z%!{mwb2obBo?h6wyFXpk;2k4Fwe-?WrYi0(wpGm2eT93}# z<71r~!$z=Hi}C;P_^(sXUu{x6I09Rk$-n_iCsXjnvr4g5HPc(uUtWIECZ68a@6^1n z396dO@qvdD&o}5NiHDSWH6uk6+TxQ$8LH7YYy$vWxxOgMjCg|lVq(nVWp3Ei*38aS zKW!#a(?)jD*!1!ySIbvq7Yzk$lulWE|prO{$(Yb-4cDf_a~5!3Zx6r^u^2c z2%F{SdVN7xU$XrC-gF;_=lwojCXK2bIn&rb9ZO^Y){4zBJ|&Zg-1nK4eDn^r$dxwp zQmI^B;$FZ}!UNk4giTc1nS>&R2-eKd)t|50-JwdTdo94s+3c>MrJ?jS*RE?NpA@xGiJSxH-pgeB{q7a9Ax+CH3*q$J0no1p!_Yc*F@xs>UZ6XP+* zEw!YkhD}>{E}m94JKwVd?Ac~D4UWJ8M`*bzy$-Waqu!l}_^3&K*fe9`#Wo3=qo)@G z%}qYpCEWJLZ}`UqLSz5$1oQU(`3|T?$n5hi&I5gko6NJ0z+3{oxx?6&)1ys)V&_7j zjY#{L6zYfl-YL{>a{Ek*oaSvC=E|=OOYo^}mL&tq`n#YwiUvU5TlIh?bb9 z{y!2I6>zVGYIczP1iPRPjQ1)X2a zWM&GDu(3=y`2lv0vebIM#~1jP>2@zJR?&6gF!K9WbJ2$Om31a$F)WBVr29s# z8x4dBavVV9+5oYeUxsb(ZKxAc8cIwq=8h*;lr@{Z0bK-p9crs_9DP3k*nkja#v(KI)IFzE{A;$OPULGF~ z QfhKDgLf=koius<4{a;H*bu_S%QC4%>%Te;0g@Q?d{LCTaUy}6vmGoKixg>hBJz>4k z-7g*Fl;j9?J{9-+9g8j1r+%{M=uKv!=qTb`5&OgXPLc&7sWA+f4ly$N-m<-l&1{_{ zBo99LuT4(>H7W1E8P`JePPFaGj+5w1mX@BPLn?V^OrzhVHYy(_N(db$AapvR0A~}6?zmzGu2X(WOX>=*} zgd7LaCRC16fMrRw2rmlQEdhDvWK<_3qtFK9ZwfxChxPN~mIP3c#>)gZ!g{FOz^D5? z^Vv`ecJ3*4P|8(dF(J2WKw(q#RLhR-^nu_J1=r?@F&{)p4>Jop_t>gsfdbv4n8S71 z?6RiJXyIvLokw2t&)>`H;j-RP*C0S~TCLjjI8WaXKu;J6jV>cit zD6V!*>jU1^w#PSHBI^k~d3wUC`P|%uhW11R`8;8K_<`N0oej#1{^sT$oB5*<&SJO! zUU@wHY*tLexzTJ(i{CH*{pe~fhGNmqjgq8+yv9vt+y0=Iz+~ZRLXsTzH(AnTG<4Z$ zYl12;hrTN2t1yl4B{yh_m5tWDBFj2fr^~jwD5n)OoL;zh8+zju(Q#BHl*!1OM&-0_ zY0$$an_H^|lSAEXoxWkdp5nGb;p)z{NoId})CryQcan{@4BgU_gi=$uN3Z8_6DPNW z!^`bNry%zus!(m#Zd3ns5?i7&r+NSM3ywfaSoN`7*sCVY=Bgd&F30G%SI~TANU7KD zQ?uA&wnoUUjo_ZH4KW3SyJ^V?{;%nk!qXd0R=_0<_F?+-LOW+exdG5#y74gdd53Fu z4<`8@dsTy9sbRKqAF971*$Q+^9}fySrr^5|C-Db$o)YeSb@?Ja?mb4xfXzer{G;K4 zu9n7c`_wd?|4nc1kyUHzGQc#d1{jQCak=dJLDucSAg!+}*QEQ~->`GFzQ$)3mq#x2 zO~0qJLk~ViL7Qv$V7w8Y2rb<1+167Q!*PcA{Z&@MF4i6XP>{OG)xSSf3;uEW`)nG( zV-Kx5@}Wkk8zRu--iqro^BlR(OWW{@m&nSn=D=}NY`Fyz6k#Vd$5nX}0$;drlD<^n z=6P9``;ommg6ZQMa_>$5Frb}F^f|2f_Y}Z=XDwpV7Ef~}paJrkpxTeFmaC==vy}J3 zO6V2j%yiDWGUFm)2lah)hK-O!lsQ@gmDNhuFKk+DALL_Lau$jOk))t~mV9wbE#N zk;=UCZP6q`TamI#rOMY?n#TEwpmC&g)=D7js%BU8tpyG{h2U`w)fET)P6Xp`!d0+Q zAx^MYos@OWD;&A#YC}HW_u=&W`}Cz!v;KVeArPGq#@~lqKr3|vaX7QEq!JAegX?5} zZIN;?oP#yUH5g#LP4DuO^{_KJR*8?*HX}_PlZqIz5PkhqZ*Cy5ZT%y36Z%1q8{JW& z`)O`%8G75h`X~baAD2Vz!DPVf;0yWX^J`DpEku|y({N0d^5YI-;}GMkxY@Un-7VIM z05Le_T6Nr}^?x^nWXZ{Fb~9?$jUFe=u&CErJUh{b-)3-uuOOj=OWc7Df%}vQ&EFzt zN*;I?-a{bCH9<2X`<>;dAZoRL6#cPax)W(psR&RX83IbU3zm}Q0sY}}?g^7@Yfr_n!yo;6|x z2g<`|SnRdp>W&iqkmzf+J^Gd%5o9rSlXE|x&`Vr&Z~>d1pkdjOq9J%TBLdu`>MS63 zhLLapUKm}k!F67Q#!<}RgFsTVr?JW;X?}!n0ejVAtVX|h3YA%i!}x1(A3$A{E^;<@ zuK&3ev3#f|qfiN5tw%bf_KYJ`XYkDB2?&gAs;fwYLaY)=oDoD=X9hb;19_xj=vjU4 zao;oC_w1w9mvKF#KyjVkmZ1_^)=P&k4X}^41LCz!C{u3LF#;vSl7+o}9m=fg%5QmE zmJ3<_JfiQ=73p*SJIcH?d1OnAa04nhDr3nAzt%qDa`@BDJ97Snsi?-a>Ukcrdim&fX&NL`je5N zuhWk-J4tL+5=&?aGw%5fY|{A>s}Yi*E202TC;-11SsD_GQs~$2q;LW#NpzCuzcV83=EYgd(uR=Ln#hjMiO7!m+SH*=z0r ztPE!1IYmo)&U`$puRLTjL*)Z#;exxr($r1}U`dD;xrWkUDg2{7PhyT_E~ zy$r79zQ?Xdm8= z2(88gsTRN?gFng{p&De3U=2^hjGdme1ZkIMw35G(Jt*gW9c;w{fr>rFxLbUcxxp-j zN%>$IRVHZH^fNP_hCmc{6jf2lIqN99YkN#jyhQ6$l z75y>OTQG0*dBo|lApq!kru3TDu>+7JM33sae-qRb;yptVvr~;@(dr~3km}1n;4s%} zH1X9Bn#t$=9c-}#AjBR8l!Ewb2C&%-(2T_4SME;c+VE8|zBE^a2DLGPdA5K6p6~hv zi9-9J^OVDgnDZ5<oi@9=!>qF@U4DW9C z4xyo2vbJbRw7?sl0RvsQ3=Bm7h+Z_ptJSO>d{#uM#%mMzEfl;ctSTv=T`OxEJ5|ul z$atYHgB(G%C@gA_w((1z@5BXwmOdgCJ^j705X$1X-mB;j^lR9$LO}&r2vccTvwwA;3lw9Veb<}H{0iI z5N9w=L~fqY{0(S*SDzCWeCD14qURC~GRvqLsV_XatG_2U9)nk&yoH>Bn>p7j;O2lx z-*}DCEc6xfgVcb@UhEmE&FJj4Ro9-3CntjKi$OB%NqQtzk`GvAoI%g+VwWK8VIx~Tt=(({<`nrWSK}{) zugL+H%kef2Rmc`aBamvGyy$UJg!Ojzs>ryoK>3mE=K<@E*BF1EpV%g2I}XyWL`&t$ z7E;)U;KEKV)dne8+i~KhTxs+t@e?F@z%6b!2}2UW?-?2Znqpprzg&y=P3HRBkdj-* zxQ69LU(gVb9OG2S{ES@81^#)0u=i!FvRXs<)YbS;8CO3sGH$*OQ5TF zm@m~CXbca-WSyE`Zn#$|5Pxg$X{ajF8jIh%^J;wF+AO*nwSN5IsqM*iQdRCZP-C;u zAt-owE_r6jyC7(6nx}`ZCVa5z<3+QIjY<$aWHY?k>FSCLq>Y}nV%@ouf{ob2Q?MjR zer+22impd!N=zYDalx-uuye~6p0V#$hV6={ippLZk@YwWzd`Y<>PH&FLC+)H56iJ= z3|}a@9YtiED+#^0EXEpTetet?Wo?M5O- zy4WiDK#6Yl-UpqZzd|PDVM?yU@s;Au%us%VJPk_0ytY`vwn0S09gvnPH|fuq|I(u> zPzge@TSKk-eXzMu}i{t$%pG43Z2swaCHB9XeU+#h>X6}~9k5K<6Y-7M4>b{GA-U4)AI z-qyn;gCeWM6ToNDho1kt8!DX0U4!bN81e}Xo`mgWngDU@kpF-F6r#pDRuhDfb%>s6 ztOsfByRpQgnu^T(RuG@0e{;POX)FHA_5s1eWi4znY&9=h@fyIM*JfiNYmHAzN>*-j zZs`Po$dN2|>c(7}=C6L=_^e)ct;;27cU)wp$Jsf(WweIC15P z3Wkg&zmB?P*TqiboOw70G8enTVktLvJGU1Kq5PY<90A5DSV_XNzFIwKeOPluj+Rq< zVmz<%*1_^3dfZWx3!%|6h6 z4}|g4cuBsSuM6mbN`d9{o#}B%HO352GFuKUABlW#X8ZQ7kT;l*R-P^P)7E|}>vT@V z*4Lc-bTbuJuv-*j0|b#)+&L%?gjBTvKQ3$#`~);%z4hiGotwks5u=+LOnS@yre0qM z%Q@}FQRap~pUQT8017FtGvZ6yWC}RsOnCcqpb(2esfN;tC1pxx(D>TJ$tC$$?(PkE z@^HQre(kx*eBoP$$VGS`YfPRGX6ZdmKY1E-(F&uc zttBf>LZoQRa<`J_allR1`-RO!j_x!<$I3gjYMyr5u<8hgj3KAt{587l=MX1E^W-m( zOny7q1Eg1=E{4fE3o6PrX>^KX@deW+lgP`29E;tG>)q3XS7lDq!Cd>duLmNxaP{U| zFl&b-u7KwS84e51BW@uB$6IMj1BaulTFo%gPv$0%fC%B3JE0y7+wDFT9XEVgRl><~ z>R97n!u({&78~(3$$%cGh+lZ!q0O)dBbA9d7J#{X$jQa4_VE%75&Z3p&EZp8e)&&j zTG3Sjn|*64Ba^-%tT1HigdOzT*Ma)et))ckJRE-ZjLt!piO2$|Y#OdIhf>7|i%SeM4k*0Mj<1o3fmf zq*rc`nzJS0PL0#w@nlY)w)N(ey}w~-hf!+Hh~Phkys*?5I{htBma9Tr=~)34R8G3C z+(5@0xw@UCP1?i%1YBzq)XPPsAg$re2y=uaDy-7|zN$7r7X_ilEdSLfPKNiAk))Up zw^fO#mY@np<`F*8#vwUX?-4sML!TDv7P7-MRnH(qo3;AG*ykb`(Yd30oSF zgJRd*dv;ahEBb}`M_8Es1Cc%(4onR0allQcdz~uZn`;HR&`e9i%Yb@Zd|k_2G@Y7S zbCI6nEuxDdKs%+Pg|rIF7qPIV#ISbTUGnmmuGl{sIPU zKA>c~`&6AF=_vm`u({KUcu03q5KI}BAu=MKPz~hLSC~Q#cn--JJ=A+Pbqolx3FRZc zJftN)3m5i=Cp2%a@dRu3XrCM+e|rdhpvlZjZ0@)>&^5#%12tm*V|`tcq`uiKs1i>N zNmN?6{&6|^iU$|Lg>CXX$x(0$^ZAtk|D`(X?44zA!;eE;OFVfB>k&!SUzEx?=t-{oFA@6HNyYcD$8-BhjDuTP~KXTNYP7 zMxB~0lfsYtF;g5o#CdyV&}neVr{a^3H8q{49jhcgg*~r2*PUWo({NFT3w40 zA)O|Scr|Zh{>A8jVb$SGPEq%&whkO`chMngS#~@D?Z;j0-0hF^ryj?T)b_uy?3knI z!n$C(9Cq!2OjTUKt5T20PL``Gd^{KKD}T*BeLTaj;>Ki?F9%jBlJkV_Q{|bidRMXe zrGXC`plb|Xavsc10Q_lk!EJU5^V`tX3V5#>S-31xx0~JGS7DRhnK{h8a*yhIt;X|v ziuxzdw)c4%g-zliYiBIWr4qyjx5b_(OlQ&sE;_d9ojJo?#2$$5vv!}i0>k%lbx%y}K#0ClWAo6QUv_}dy@E~NG0b%@THMk|+IYxVg4 zs$9AUoP%12$A)}(-g#Y6{@TG(OCn+GAad~tBZ4rpIY4?$9aM^SA|&~mRkiL0!{2rp z^b?0GQK>nR?__9hqG|9X$pnB^KD90g`Py>_*)6Rmb=r(W{6E;MUNKX2C>4s=o1;*T z6c!JWu>8vahv{)?>;&JQ33KH^dX3DwZc{|%kGMo<8?m`qmiIIV-iA2ZW`dsxt5qc1 z6UM`3_!Ga&rza8+c;_r-#Qvc1+~$G9lSwzJ)R?I7W;JY@4>Ug_q-BzunM9}O=8VP{ zOQX|7a-n@4*@+EV_E@m@>_jT&v!7ORi7uIxO|4 zA(#5%sKLRYfEH|_La(dM&*PxEM(zmKYZlm zkaqaj2@eg-xty=n$2kVMxTTer(4HW#lS7o5zr{+e*Y45HI{&d4`n;a3(cY?jqMr9S ztxg$w(%fEqH+Z0i8l`LJR=n^C_uAZ!ICkdfMi6%)XF}fjIPV^AVA+rq$EnVRzm6K! z6YuaMa{}t440NVuM@=V`M-)AxdYJV; zQp4R>+Up&Y2PMq*VZQ!=k{yzGX8KtBa?kXWo`ex)bPz1ws^dxXL>Ii-ImylQo~gRu zFL3{Kr^nq(+(_PNA0~svj#t+nt!NS)m2Hp*Q%6F*=X3#OzG?1?S2q3p!qE){JaHJn zys1yOrPp)cR)!qLhTo>@!mHA#%wI2UXL53CUDNhpXHuz5fB~!>k`68HZvs>v+Q`)>8ei&{wW^x;gU)8e zn25~Tya@1~P8%L@p$lq}<$6D&B&dRNYfsB5G0$Vfs1_)%-;>4O$iF^hHk_YGw#CkU zywUPghP8*`BEw%i6EjjooBpLuk%jqmvzyh2$2^?e-;7SC&OaV~GZ&>U$l7m~e2KL- z<()Pim-wftG12$qQ!2Gvy&mqJOPA_mOJE~Rxc0R8j@riQQm7d_cW0?3DZuix>0jS& z=hS-+BxWasUm3<#g&YfYOJjun^W8+lEBI5;_=hOXo=3~7e9H7W_8g_P#AqX)R>^m% z3Hm)BNRNxKT!n$rtoZoaagr#*fFHVI zp+~NVt9uOuIbFT%%)QKw_GM0IeP3^Hlo`y`<4%2_4vMwsFjIxXydh4)K796CH$`IK z2HSMPG+pn}l-Dq)!Jz2Vx+GWAbfUs?x9LV0w-mY})-du5Km$slyhCy8nWIXFy4qhq zXlja{TDSB0w6?qb`9gBQH$Fo?p~Di~R8Rt|o<=5{n`1Z5f-GLkZlTvquP#Hz^tg!~ z6Qfqd4Oo<-}0Q&7H$uYCvH^mm+s*tHqPakT&p(in=31#ljj*$mAFS2?D z;o}8Om}OnIU&9Sgc=yh^4jNH@7-iLb{IW#*du&E{&jsIa>=ys$bJjRZEql>k6Ur!lx3Jk0-WqDrlb|%z9pL>y??^)1 z#w$-xo|x4KK|ZEj>wwv+WYxCt1qGzzjZQ{;KMa{CY;A_ zwqda59*yAXmGbWX6FM00le;g*A7*$Fyxr?Dr9|C=&tjQ3Z~K{cY+l%`XoT#axkopr zf7R}Bm9LLqms13{36rP-1*z5Td=(o96OLp0Nt&N&AjaQ~TT2@zx7oOvMdn`%RK>mO zj_W_6iczw2lZp#s-L^K_)t9cn3~xQ6a9_hR>kC0tb+Nx~;<8|>pO@R_eT|3~j(8^b zU;PD4p{mp|3#fHugX|zHFGICE&Cd&G*^*r&H9tc+qc`^?N;6%R$qM5e%b;yx`;jZi z^KcWw_}!>7KxQ*?PN1t1BtwQN{@BnoJzAnF;$5+ zH|G_o)csC%^$Gj<3+$r@u`^Yk8302Az4@Z6HTYJ(R=zXrqpg>50Br|!T_I!q4|B_C zKDVPc&x7@Il{#hFB55SHXzp9fUJc$vbSHpVW+~La$WpK>;iTh4$BoJ`m-IqEf~&yM zQVW?NH@LH5M=ZGcKUaSM*TbllZ?VVt8vw-ZMd83c?e0r2OgdP;5+vrkotp?+fSFRy zG*zPIpv1_2`qC0LmOw~q2Wl_;+rO(R4S@TeJ>a6@|HIS1|7P!8{!gD8^|duqK9N^~ wWih3x|Gu6|FTBA37q|aUpHKep>3BmKliuOhd$VW&JU@lutn4f+kNaNzA0WX7od5s; literal 0 HcmV?d00001 diff --git a/docimg/README.txt b/docimg/README.txt new file mode 100644 index 00000000..ab7f16e4 --- /dev/null +++ b/docimg/README.txt @@ -0,0 +1 @@ +Images used in Doxygen documentation \ No newline at end of file diff --git a/src/app/main.cpp b/src/app/main.cpp index 389de93f..97cee634 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -48,13 +48,22 @@ #include #include -/* Doxygen main page */ - /** - \mainpage -Doxygen documentation of Colobot project +Doxygen documentation of Colobot: Gold Edition project. + +Colobot (COLOnize with BOTs) is a game combining elements of real time strategy (RTS) +and educational game, aiming to teach programming through entertainment. You are playing as an astronaut +on a journey with robot helpers to find a planet for colonization. It features a C++ and Java-like, +object-oriented language, CBOT, which can be used to program the robots available in the game. + +The original version of the game was developed by [Epsitec](http://www.epsitec.ch/) and released in 2001. +Later, in 2005 another version named Ceebot was released. In March 2012, through attempts +by Polish Colobot fans, Epsitec agreeed to release the source code of the game on GPLv3 license. +The license was given specfifically to our community, TerranovaTeam, +part of International Colobot Community (ICC) (previously known as Polish Portal of Colobot (PPC); +Polish: Polski Portal Colobota) with our website at http://colobot.info/. \section Intro Introduction @@ -71,17 +80,21 @@ The source code was split from the original all-in-one directory to subdirectori each containing one major part of the project. The current layout is the following: - src/CBot - separate library with CBot language - - src/app - class CApplication and everything concerned with SDL plus other system-dependent - code such as displaying a message box, finding files, etc. + - src/app - class CApplication and everything concerned with SDL - src/common - shared structs, enums, defines, etc.; should not have any external dependencies + - src/common/resources - filesystem management using PHYSFS library + - src/common/system - system-dependent code such as displaying a message box, finding files, etc. + - src/common/thread - wrapper classes for SDL threads - src/graphics/core - abstract interface of graphics device (abstract CDevice class) - (split from old src/graphics/common) - src/graphics/engine - main graphics engine based on abstract graphics device; is composed - of CEngine class and associated classes implementing the 3D engine (split from old src/graphics/common) + of CEngine class and associated classes implementing the 3D engine + - src/graphics/model - code related to loading/saving model files - src/graphics/opengl - concrete implementation of CDevice class in OpenGL: CGLDevice - src/graphics/d3d - in (far) future - perhaps a newer implementation in DirectX (9? 10?) - src/math - mathematical structures and functions - - src/object - non-graphical game engine, that is robots, buildings, etc. + - src/object - non-grphical game object logic, that is robots, buildings, etc. + - src/level - main part of non-graphical game engine, that is loading levels etc. + - src/level/parser - parser for loading/saving level files from format known as scene files - src/ui - 2D user interface (menu, buttons, check boxes, etc.) - src/sound - sound and music engine written using fmod library - src/physics - physics engine diff --git a/src/graphics/README.txt b/src/graphics/README.txt index f9ec2ae8..41772be2 100644 --- a/src/graphics/README.txt +++ b/src/graphics/README.txt @@ -11,3 +11,48 @@ * defining a border between pure graphics engine and other parts of application. */ +/** + * \page graphics Graphics engine + * + * The graphics engine consists of 3 parts: + * * core - low-level device code (currently with only OpenGL implementation) + * * main engine - managing and displaying 3D environment (terrain, models, water, sky, effects, camera, etc.) + * * 2D interface - classes drawing the 2D interface (menus, buttons, editor, HUD elements) + * + * \section coords Drawing coordinates + * + * \subsection coords2d 2D interface + * + * 2D interface is drawn by setting orthogonal projection yielding the following 2D coordinate system: + * + * \image html 2d_coord.png + * + * Depth test is disabled for 2D interface, so Z coordinates are irrelevant. + * + * The coordinate system is constant and is independent of resolution or screen proportions. + * + * UI elements are laid out by computing these standard coordinates, using 640x480 resoultion as reference. + * That is, their coordinates are computed like so: x = 32.0f/640.0f, y = 400.0f/480.0f. + * + * \subsection coords3d 3D environment + * + * 3D environment is drawn using the following coordinate system: + * + * \image html 3d_canonical_coords.png + * + * The base coordinate system is like depicted above with viewport on Z=0 plane. + * The coordinates are then transformed by world, view and projection matrices to yield screen coordinates. + * + * The base coordinates are also model coordinates. All models must be modelled in this setup. + * Scale should be kept proportional to existing models. + * + * The world matrix defines the transformation from model coordinate system to the point and orientation + * in 3D scene. This matrix is defined one per every graphics engine object. + * (Note the emphasis - the objects as defined in game engine do not necessarily correspond to + * one graphics engine object.) + * + * The view and projection matrices define the viewing point and volume, and are mostly managed by Gfx::CCamera. + * View is defined by Math::LoadViewMatrix() function, that is using 3 vectors: eye position (eyePt), + * target point (lookatPt) and up vector (upVec). Projection is always perspective, + * with changing view angle (focus). + */ \ No newline at end of file diff --git a/src/graphics/model/README.txt b/src/graphics/model/README.txt new file mode 100644 index 00000000..a71ffa9f --- /dev/null +++ b/src/graphics/model/README.txt @@ -0,0 +1,82 @@ +/** + * \namespace Gfx::ModelInput + * \brief Functions related to model loading + */ + +/** + * \namespace Gfx::ModelOutput + * \brief Functions related to model saving + */ + +/** + * \page models Models + * + * Model formats and associated issues are described briefly for graphics designers and developers. + * + * \section format Model format + * + * \todo Update for the new model format + * + * Colobot models are basically a collection of triangles with some associated data. + * In the code, the class Gfx::CModel (src/graphics/model/model.h) + * is responsible for reading/writing model files. Each triangle of model contains the information + * as stored in Gfx::ModelTriangle struct defined in model_triangle.h header, that is: + * * 3 triangle points (coordinates, normals, UV texture coordinates for 2 textures) + * * material (ambient, diffuse, specular colors) + * * file names for 1st and 2nd texture (or, for 2nd texture - variable flag) + * * rendering state + * * min and max values of LOD (= level of details) + * + * \subsection textures Textures + * + * 1st texture is always static - the assigned image name. + * + * 2nd texture can be set explicitly in 2nd texture name, with variable flag set to false. + * It is then static as 1st texture. + * + * But if variable flag is set, the texture will be applied dynamically by the graphics engine. + * It will be one of dirtyXX.png textures, depending on current setup. + * + * \subsection renderstates Rendering states + * + * Rendering state is one of Gfx::CEngine's rendering states, that is a mask of enum Gfx::EngineRenderState values + * from src/graphics/engine/engine.h. + * + * For most purposes, the default render (Gfx::ENG_RSTATE_NORMAL = 0) state will be sufficient. + * This state enables regular one-texture rendering. + * + * To use 2nd texture, set one of Gfx::ENG_RSTATE_DUAL_BLACK or Gfx::ENG_RSTATE_DUAL_WHITE states. + * Other states, enabling specific behavior may be used in rare cases. + * + * \subsection lod Min and max LOD + * + * LOD is used to display different model triangles based + * on distance to viewer. The given triangle will only be displayed if the distance is within bounds [min, max]. + * + * For now, use standard definitions of min and max which + * fall into 3 categories: + * * min = 0, max = 100 - max detail + * * min = 100, max = 200 - medium detail + * * min = 200, max = 1 000 000 - low detail + * + * \section fileformats File formats + * + * There are currently 3 file formats recognized by Gfx::ModelInput and Gfx::ModelOutput: + * * old binary format (in 3 versions, though mostly only the 3rd one is used) - this is the format + * of original model files; it is deprecated now and will be removed in the future + * * new text format - preferred for now, as it is easy to handle and convert to other formats as necessary + * * new binary format - contains the same information as new text format + * + * \section blenderimport Import/export in Blender + * + * The plugin to import and export models in Blender is contained in \p tools/blender-scipts.py. + * To use it, install it as per instructions [on Blender wiki](http://wiki.blender.org/index.php/Doc:2.6/Manual/Extensions/Python/Add-Ons). + * It will register new menu entries under File -> Import and File -> Export. Import is always active, but to export, + * you have to select a mesh object first. + * + * Textures are loaded from the same directory as the model file. + * + * Additional data like state, variable texture flag and min and max LOD can be given as user attributes. + * + * If you have any problems, please contact \b piotrdz on ICC forum or IRC channels. + */ From e48188b42931b34dbc753b47aca3b5f198e9eac6 Mon Sep 17 00:00:00 2001 From: melex750 Date: Sat, 6 Aug 2016 04:56:57 -0400 Subject: [PATCH 19/56] Fix failed assert when literal null is returned --- src/CBot/CBotInstr/CBotExprRetVar.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/CBot/CBotInstr/CBotExprRetVar.cpp b/src/CBot/CBotInstr/CBotExprRetVar.cpp index 28b0fa83..223058eb 100644 --- a/src/CBot/CBotInstr/CBotExprRetVar.cpp +++ b/src/CBot/CBotInstr/CBotExprRetVar.cpp @@ -135,9 +135,14 @@ bool CBotExprRetVar::Execute(CBotStack* &pj) { pVar = pj->GetVar(); pVar->Update(pj->GetUserPtr()); + if (pVar->GetType(CBotVar::GetTypeMode::CLASS_AS_POINTER) == CBotTypNullPointer) + { + pile1->SetError(CBotErrNull, &m_token); + return pj->Return(pile1); + } if ( !m_next3->ExecuteVar(pVar, pile, &m_token, true, false) ) - return false; + return false; if (pVar) pile1->SetCopyVar(pVar); From f3bf56d9e3da0f27a05839bde7e99e1f1e5d650e Mon Sep 17 00:00:00 2001 From: melex750 Date: Sat, 6 Aug 2016 05:22:40 -0400 Subject: [PATCH 20/56] Add unit tests for returned object member access --- test/unit/CBot/CBot_test.cpp | 251 +++++++++++++++++++++++++++++++++++ 1 file changed, 251 insertions(+) diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index 2d96d59e..a9af2420 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -1188,3 +1188,254 @@ TEST_F(CBotUT, AccessMembersInParameters_Issue256) "}\n" ); } + +TEST_F(CBotUT, InstrCallAccessMemberVoid) +{ + ExecuteTest( + "void Test() {}\n" + "extern void TestAccessMemberVoid() {\n" + " Test().x;\n" + "}\n", + CBotErrNoTerminator + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberNonObject) +{ + ExecuteTest( + "int GetInt() {\n" + " return 1;\n" + "}\n" + "extern void TestAccessMemberNonObject() {\n" + " GetInt().x;\n" + "}\n", + CBotErrNoTerminator + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberObjectNull) +{ + ExecuteTest( + "public class TestClass { int x = 1; }\n" + "TestClass GetObjectNull() {\n" + " TestClass t = null;" + " return t;\n" + "}\n" + "extern void TestAccessMemberObjectNull() {\n" + " GetObjectNull().x;\n" + "}\n", + CBotErrNull + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberReturnNull) +{ + ExecuteTest( + "public class TestClass { int x = 1; }\n" + "TestClass GetReturnNull() {\n" + " return null;\n" + "}\n" + "extern void TestAccessMemberReturnNull() {\n" + " GetReturnNull().x;\n" + "}\n", + CBotErrNull + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberNotVar) +{ + ExecuteTest( + "public class TestClass {}\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberNotVar() {\n" + " TestClass tc();\n" + " GetObject(tc).123;\n" + "}\n", + CBotErrUndefClass + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberVarNonMember) +{ + ExecuteTest( + "public class TestClass { int x = 1; }\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberVarNonMember() {\n" + " TestClass tc();\n" + " GetObject(tc).y;\n" + "}\n", + CBotErrUndefItem + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberVarUndefined) +{ + ExecuteTest( + "public class TestClass { int x; }\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberVarUndefined() {\n" + " TestClass tc();\n" + " GetObject(tc).x;\n" + "}\n", + CBotErrNotInit + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberVarPrivate) +{ + ExecuteTest( + "public class TestClass { private int x = 123; }\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberVarPrivate() {\n" + " TestClass tc();\n" + " ASSERT(123 == GetObject(tc).x);\n" + "}\n", + CBotErrPrivate + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberVar) +{ + ExecuteTest( + "public class TestClass { int x = 123; }\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberVar() {\n" + " TestClass tc();\n" + " ASSERT(123 == GetObject(tc).x);\n" + "}\n" + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberVarArrayBadIndex) +{ + ExecuteTest( + "public class TestClass { int[] a; }\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberVarArrayEmpty() {\n" + " TestClass tc();\n" + " int i = GetObject(tc).a[4.7];\n" + "}\n", + CBotErrBadIndex + ); +} +TEST_F(CBotUT, InstrCallAccessMemberVarArrayCloseIndex) +{ + ExecuteTest( + "public class TestClass { int[] a = {123}; }\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberVarArrayEmpty() {\n" + " TestClass tc();\n" + " int i = GetObject(tc).a[0;\n" + "}\n", + CBotErrCloseIndex + ); +} +TEST_F(CBotUT, InstrCallAccessMemberVarArrayEmpty) +{ + ExecuteTest( + "public class TestClass { int[] a; }\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberVarArrayEmpty() {\n" + " TestClass tc();\n" + " int i = GetObject(tc).a[0];\n" + "}\n", + CBotErrOutArray + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberVarArrayOutOfRange) +{ + ExecuteTest( + "public class TestClass { int a[] = {123}; }\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberVarArrayOut() {\n" + " TestClass tc();\n" + " int i = GetObject(tc).a[1];\n" + "}\n", + CBotErrOutArray + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberVarArray) +{ + ExecuteTest( + "public class TestClass { int a[] = {123}; }\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberVarArray() {\n" + " TestClass tc();\n" + " ASSERT(123 == GetObject(tc).a[0]);\n" + "}\n" + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberMethod) +{ + ExecuteTest( + "public class TestClass {\n" + " int x = 123;\n" + " int testGetX() { return x; }\n" + "}\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberMethod() {\n" + " TestClass tc();\n" + " ASSERT(123 == GetObject(tc).testGetX());\n" + "}\n" + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberMethodChain) +{ + ExecuteTest( + "public class TestClass {\n" + " int x = 123;\n" + " TestClass testGetThis() { return this; }\n" + " int testGetX() { return x; }\n" + "}\n" + "TestClass GetObject(TestClass t) {\n" + " return t;\n" + "}\n" + "extern void TestAccessMemberMethodChain() {\n" + " TestClass tc();\n" + " ASSERT(123 == GetObject(tc).testGetThis().testGetX());\n" + "}\n" + ); +} + +TEST_F(CBotUT, InstrCallAccessMemberNewObjectDestructor) +{ + ExecuteTest( + "public class TestClass {\n" + " int x = 123;\n" + " static bool b = false;\n" + " void ~TestClass() { b = true; }\n" + "}\n" + "TestClass GetNewObject() { return new TestClass(); }\n" + "extern void TestAccessMemberNewObject() {\n" + " TestClass tc();\n" + " ASSERT(123 == GetNewObject().x);\n" + " ASSERT(tc.b == true);\n" + "}\n" + ); +} From a205eace38d234feeb20a3feb75ce62caa03c0c4 Mon Sep 17 00:00:00 2001 From: melex750 Date: Sat, 6 Aug 2016 16:29:02 -0400 Subject: [PATCH 21/56] Add missing license headers --- src/CBot/CBotInstr/CBotExprRetVar.cpp | 18 ++++++++++++++++++ src/CBot/CBotInstr/CBotExprRetVar.h | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/CBot/CBotInstr/CBotExprRetVar.cpp b/src/CBot/CBotInstr/CBotExprRetVar.cpp index 223058eb..9a1eeb72 100644 --- a/src/CBot/CBotInstr/CBotExprRetVar.cpp +++ b/src/CBot/CBotInstr/CBotExprRetVar.cpp @@ -1,3 +1,21 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * 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 3 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, see http://gnu.org/licenses + */ #include #include "CBot/CBotInstr/CBotExprRetVar.h" diff --git a/src/CBot/CBotInstr/CBotExprRetVar.h b/src/CBot/CBotInstr/CBotExprRetVar.h index 79c72ac6..a1c37f55 100644 --- a/src/CBot/CBotInstr/CBotExprRetVar.h +++ b/src/CBot/CBotInstr/CBotExprRetVar.h @@ -1,3 +1,21 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * 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 3 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, see http://gnu.org/licenses + */ #pragma once From 6b8e240d26ea07b79e28899577ee2942a141b59d Mon Sep 17 00:00:00 2001 From: Jeremy Mickelson Date: Sun, 7 Aug 2016 08:27:01 -0400 Subject: [PATCH 22/56] Fix implicit downcast with variable initialization --- src/CBot/CBotInstr/CBotDefClass.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/CBot/CBotInstr/CBotDefClass.cpp b/src/CBot/CBotInstr/CBotDefClass.cpp index 4c9ea4d4..bfd0dedf 100644 --- a/src/CBot/CBotInstr/CBotDefClass.cpp +++ b/src/CBot/CBotInstr/CBotDefClass.cpp @@ -174,7 +174,8 @@ CBotInstr* CBotDefClass::Compile(CBotToken* &p, CBotCStack* pStack, CBotClass* p CBotClass* result = pStk->GetClass(); if ( !pStk->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_POINTER).Eq(CBotTypNullPointer) && ( !pStk->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_POINTER).Eq(CBotTypPointer) || - ( result != nullptr && !result->IsChildOf(pClass) ))) // type compatible ? + ( result != nullptr && !(pClass->IsChildOf(result) || + result->IsChildOf(pClass))))) // type compatible ? { pStk->SetError(CBotErrBadType1, p->GetStart()); goto error; @@ -268,9 +269,10 @@ bool CBotDefClass::Execute(CBotStack* &pj) // evaluates the expression for the assignment if (!m_expr->Execute(pile)) return false; + CBotVar* pv = pile->GetVar(); + if ( bIntrincic ) { - CBotVar* pv = pile->GetVar(); if ( pv == nullptr || pv->GetPointer() == nullptr ) { pile->SetError(CBotErrNull, &m_token); @@ -280,8 +282,17 @@ bool CBotDefClass::Execute(CBotStack* &pj) } else { + if ( !(pv == nullptr || pv->GetPointer() == nullptr) ) + { + if ( !pv->GetClass()->IsChildOf(pClass)) + { + pile->SetError(CBotErrBadType1, &m_token); + return pj->Return(pile); + } + } + CBotVarClass* pInstance; - pInstance = (static_cast(pile->GetVar()))->GetPointer(); // value for the assignment + pInstance = pv->GetPointer(); // value for the assignment CBotTypResult type = pThis->GetTypResult(); pThis->SetPointer(pInstance); pThis->SetType(type); // keep pointer type From 8b3c4302d2b8872fe3cd10833c01c0c697fb8081 Mon Sep 17 00:00:00 2001 From: melex750 Date: Sun, 7 Aug 2016 10:01:10 -0400 Subject: [PATCH 23/56] Add more unit tests for inheritance --- test/unit/CBot/CBot_test.cpp | 129 +++++++++++++++++++++++++++-------- 1 file changed, 102 insertions(+), 27 deletions(-) diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index de07c2ab..6d5954a6 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -1006,6 +1006,26 @@ TEST_F(CBotUT, ClassStringAdd_Issue535) ); } +TEST_F(CBotUT, ClassInheritanceAssignment) +{ + ExecuteTest( + "public class BaseClass {}\n" + "public class MidClass extends BaseClass {}\n" + "public class SubClass extends MidClass {}\n" + "extern void ClassInheritanceVars()\n" + "{\n" + " BaseClass bc = new MidClass();\n" + " MidClass mc = bc;\n" + " mc = new SubClass();\n" + " SubClass sc = mc;\n" + " bc = mc;\n" + " bc = new MidClass();\n" + " bc = new SubClass();\n" + " sc = bc;\n" + "}\n" + ); +} + TEST_F(CBotUT, ClassInheritanceVars) { ExecuteTest( @@ -1041,7 +1061,16 @@ TEST_F(CBotUT, ClassInheritanceVars) " ASSERT(sc.c == 1617);\n" " ASSERT(sc.d == 1819);\n" " ASSERT(sc.e == 2021);\n" - // TODO Add tests for polymorphism here + // Test polymorphism + " bc = mc;\n" + " ASSERT(bc.a == 123);\n" + " ASSERT(bc.b == 456);\n" + " ASSERT(bc.c == 789);\n" + " mc = sc;\n" + " ASSERT(mc.a == 123);\n" + " ASSERT(mc.b == 1011);\n" + " ASSERT(mc.c == 1213);\n" + " ASSERT(mc.d == 1415);\n" "}\n" ); } @@ -1078,6 +1107,14 @@ TEST_F(CBotUT, ClassInheritanceMethods) " ASSERT(789 == testInsideBaseClass());\n" " return c;\n" " }\n" + " int testSuper() {\n" + " ASSERT(super.a == 123);\n" + " ASSERT(super.b == 456);\n" + " ASSERT(super.c == 789);\n" + " ASSERT(123 == super.testOverride());\n" + " ASSERT(789 == super.testInsideBaseClass());\n" + " return super.testInsideBaseOverride();\n" + " }\n" " int testInsideMidOverride() { return testOverride(); }\n" "}\n" "public class SubClass extends MidClass {\n" @@ -1096,6 +1133,16 @@ TEST_F(CBotUT, ClassInheritanceMethods) " ASSERT(1213 == testInsideMidClass());\n" " return c;\n" " }\n" + " int testSuper() {\n" + " ASSERT(super.a == 123);\n" + " ASSERT(super.b == 1011);\n" + " ASSERT(super.c == 1213);\n" + " ASSERT(super.d == 1415);\n" + " ASSERT(1011 == super.testOverride());\n" + " ASSERT(789 == super.testInsideBaseClass());\n" + " ASSERT(1213 == super.testInsideMidClass());\n" + " return super.testSuper();\n" + " }\n" " int testInsideSubOverride() { return testOverride(); }\n" "}\n" "extern void InheritanceMethods()\n" @@ -1106,6 +1153,7 @@ TEST_F(CBotUT, ClassInheritanceMethods) " ASSERT(789 == bc.testInsideBaseClass());\n" " ASSERT(123 == bc.testInsideBaseOverride());\n" " MidClass mc();\n" + " ASSERT(1011 == mc.testSuper());\n" " ASSERT(1011 == mc.testOverride());\n" " ASSERT(456 == mc.testNoOverride());\n" " ASSERT(789 == mc.testInsideBaseClass());\n" @@ -1113,6 +1161,7 @@ TEST_F(CBotUT, ClassInheritanceMethods) " ASSERT(1011 == mc.testInsideBaseOverride());\n" " ASSERT(1011 == mc.testInsideMidOverride());\n" " SubClass sc();\n" + " ASSERT(1617 == sc.testSuper());\n" " ASSERT(1617 == sc.testOverride());\n" " ASSERT(456 == sc.testNoOverride());\n" " ASSERT(789 == sc.testInsideBaseClass());\n" @@ -1121,7 +1170,22 @@ TEST_F(CBotUT, ClassInheritanceMethods) " ASSERT(1617 == sc.testInsideBaseOverride());\n" " ASSERT(1617 == sc.testInsideMidOverride());\n" " ASSERT(1617 == sc.testInsideSubOverride());\n" - // TODO Add tests for polymorphism here + // Test polymorphism + " bc = mc;\n" + " ASSERT(1011 == bc.testOverride());\n" + " ASSERT(789 == bc.testInsideBaseClass());\n" + " ASSERT(1011 == bc.testInsideBaseOverride());\n" + " bc = sc;\n" + " ASSERT(1617 == bc.testOverride());\n" + " ASSERT(789 == bc.testInsideBaseClass());\n" + " ASSERT(1617 == bc.testInsideBaseOverride());\n" + " mc = sc;\n" + " ASSERT(1617 == mc.testSuper());\n" + " ASSERT(1617 == mc.testOverride());\n" + " ASSERT(789 == mc.testInsideBaseClass());\n" + " ASSERT(1213 == mc.testInsideMidClass());\n" + " ASSERT(1617 == mc.testInsideBaseOverride());\n" + " ASSERT(1617 == mc.testInsideMidOverride());\n" "}\n" ); } @@ -1141,6 +1205,7 @@ TEST_F(CBotUT, ClassInheritanceTestThis) " ASSERT(this.b == 456);\n" " ASSERT(this.c == 789);\n" " }\n" + " BaseClass testSuperReturnThis(){ return this; }\n" " BaseClass testReturnThisFromBaseClass() { return this; }\n" "}\n" "public class MidClass extends BaseClass {\n" @@ -1157,6 +1222,7 @@ TEST_F(CBotUT, ClassInheritanceTestThis) " ASSERT(this.c == 1213);\n" " ASSERT(this.d == 1415);\n" " }\n" + " MidClass testSuperReturnThis(){ return super.testSuperReturnThis(); }\n" " MidClass testReturnThisFromMidClass() { return this; }\n" "}\n" "public class SubClass extends MidClass {\n" @@ -1175,39 +1241,48 @@ TEST_F(CBotUT, ClassInheritanceTestThis) " ASSERT(this.d == 1819);\n" " ASSERT(this.e == 2021);\n" " }\n" + " SubClass testSuperReturnThis(){ return super.testSuperReturnThis(); }\n" " SubClass testReturnThisFromSubClass() { return this; }\n" "}\n" "extern void ClassInheritanceTestThis()\n" "{\n" - " BaseClass b();\n" - " b.testBaseMembersAndParams(-1, -2, -3);\n" - " BaseClass bc = b.testReturnThisFromBaseClass();\n" - " ASSERT(bc == b);\n" - " MidClass m();\n" - " m.testBaseMembersAndParams(-1, -2, -3);\n" - " m.testMidMembersAndParams(-1, -2, -3, -4);\n" - " MidClass mc = m.testReturnThisFromMidClass();\n" - " ASSERT(mc == m);\n" - " mc = m.testReturnThisFromBaseClass();\n" - " ASSERT(mc == m);\n" - " SubClass s();\n" - " s.testBaseMembersAndParams(-1, -2, -3);\n" - " s.testMidMembersAndParams(-1, -2, -3, -4);\n" - " s.testSubMembersAndParams(-1, -2, -3, -4, -5);\n" - " SubClass sc = s.testReturnThisFromSubClass();\n" - " ASSERT(sc == s);\n" - " sc = s.testReturnThisFromBaseClass();\n" - " ASSERT(sc == s);\n" - " sc = s.testReturnThisFromMidClass();\n" - " ASSERT(sc == s);\n" - // TODO Add tests for polymorphism here + " BaseClass bc();\n" + " MidClass mc();\n" + " SubClass sc();\n" + " ASSERT(bc == bc.testSuperReturnThis());\n" + " ASSERT(bc == bc.testReturnThisFromBaseClass());\n" + " bc.testBaseMembersAndParams(-1, -2, -3);\n" + " ASSERT(mc == mc.testSuperReturnThis());\n" + " ASSERT(mc == mc.testReturnThisFromBaseClass());\n" + " ASSERT(mc == mc.testReturnThisFromMidClass());\n" + " mc.testBaseMembersAndParams(-1, -2, -3);\n" + " mc.testMidMembersAndParams(-1, -2, -3, -4);\n" + " ASSERT(sc == sc.testSuperReturnThis());\n" + " ASSERT(sc == sc.testReturnThisFromBaseClass());\n" + " ASSERT(sc == sc.testReturnThisFromMidClass());\n" + " ASSERT(sc == sc.testReturnThisFromSubClass());\n" + " sc.testBaseMembersAndParams(-1, -2, -3);\n" + " sc.testMidMembersAndParams(-1, -2, -3, -4);\n" + " sc.testSubMembersAndParams(-1, -2, -3, -4, -5);\n" + // Test polymorphism + " bc = mc;\n" + " ASSERT(mc == bc.testSuperReturnThis());\n" + " ASSERT(mc == bc.testReturnThisFromBaseClass());\n" + " bc.testBaseMembersAndParams(-1, -2, -3);\n" + " bc = sc;\n" + " ASSERT(sc == bc.testSuperReturnThis());\n" + " ASSERT(sc == bc.testReturnThisFromBaseClass());\n" + " bc.testBaseMembersAndParams(-1, -2, -3);\n" + " mc = sc;\n" + " ASSERT(sc == mc.testSuperReturnThis());\n" + " ASSERT(sc == mc.testReturnThisFromBaseClass());\n" + " ASSERT(sc == mc.testReturnThisFromMidClass());\n" + " mc.testBaseMembersAndParams(-1, -2, -3);\n" + " mc.testMidMembersAndParams(-1, -2, -3, -4);\n" "}\n" ); } -// TODO TEST_F(CBotUT, ClassInheritanceTestSuper) - - TEST_F(CBotUT, String) { ExecuteTest( From 66218319ddeb7aa5fd3a236c2c1641679e554237 Mon Sep 17 00:00:00 2001 From: melex750 Date: Tue, 9 Aug 2016 11:59:07 -0400 Subject: [PATCH 24/56] Add support for circular references Issue #433 --- src/CBot/CBotClass.cpp | 33 +++++++++++++++++++++++++++++++-- src/CBot/CBotClass.h | 10 ++++++++++ src/CBot/CBotProgram.cpp | 4 ++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/CBot/CBotClass.cpp b/src/CBot/CBotClass.cpp index 70c72ef5..45510763 100644 --- a/src/CBot/CBotClass.cpp +++ b/src/CBot/CBotClass.cpp @@ -504,16 +504,25 @@ CBotClass* CBotClass::Compile1(CBotToken* &p, CBotCStack* pStack) classe->Purge(); // empty the old definitions // TODO: Doesn't this remove all classes of the current program? classe->m_IsDef = false; // current definition + classe->m_pOpenblk = p; + if ( !IsOfType( p, ID_OPBLK) ) { pStack->SetError(CBotErrOpenBlock, p); return nullptr; } - while ( pStack->IsOk() && !IsOfType( p, ID_CLBLK ) ) + int level = 1; + do // skip over the definition { - classe->CompileDefItem(p, pStack, false); + int type = p->GetType(); + p = p->GetNext(); + if (type == ID_OPBLK) level++; + if (type == ID_CLBLK) level--; } + while (level > 0 && p != nullptr); + + if (level > 0) pStack->SetError(CBotErrCloseBlock, classe->m_pOpenblk); if (pStack->IsOk()) return classe; } @@ -521,6 +530,26 @@ CBotClass* CBotClass::Compile1(CBotToken* &p, CBotCStack* pStack) return nullptr; } +//////////////////////////////////////////////////////////////////////////////// +void CBotClass::DefineClasses(CBotClass* pClass, CBotCStack* pStack) +{ + while (pClass != nullptr) + { + CBotClass* pParent = pClass->m_parent; + pClass->m_nbVar = (pParent == nullptr) ? 0 : pParent->m_nbVar; + CBotToken* p = pClass->m_pOpenblk->GetNext(); + + while (pStack->IsOk() && !IsOfType(p, ID_CLBLK)) + { + pClass->CompileDefItem(p, pStack, false); + } + + if (!pStack->IsOk()) return; + + pClass = pClass->GetNext(); + } +} + //////////////////////////////////////////////////////////////////////////////// bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond) { diff --git a/src/CBot/CBotClass.h b/src/CBot/CBotClass.h index 234f5786..2fc494dc 100644 --- a/src/CBot/CBotClass.h +++ b/src/CBot/CBotClass.h @@ -291,6 +291,14 @@ public: static CBotClass* Compile1(CBotToken* &p, CBotCStack* pStack); + /*! + * \brief DefineClasses Calls CompileDefItem for each class in a list + * of classes, defining fields and pre-compiling methods. + * \param pClass List of classes + * \param pStack + */ + static void DefineClasses(CBotClass* pClass, CBotCStack* pStack); + /*! * \brief CompileDefItem * \param p @@ -385,6 +393,8 @@ private: CBotFunction* m_pMethod; void (*m_rUpdate)(CBotVar* thisVar, void* user); + CBotToken* m_pOpenblk; + //! How many times the program currently holding the lock called Lock() int m_lockCurrentCount = 0; //! Programs waiting for lock. m_lockProg[0] is the program currently holding the lock, if any diff --git a/src/CBot/CBotProgram.cpp b/src/CBot/CBotProgram.cpp index d0284ea2..77dac399 100644 --- a/src/CBot/CBotProgram.cpp +++ b/src/CBot/CBotProgram.cpp @@ -99,6 +99,10 @@ bool CBotProgram::Compile(const std::string& program, std::vector& else m_functions->AddNext(next); } } + + // Define fields and pre-compile methods for each class in this program + if (pStack->IsOk()) CBotClass::DefineClasses(m_classes, pStack.get()); + if ( !pStack->IsOk() ) { m_error = pStack->GetError(m_errorStart, m_errorEnd); From 1546c1514a86457bb945dcdf205e26d35f002fc6 Mon Sep 17 00:00:00 2001 From: aleksei-sh Date: Sun, 17 Jul 2016 17:28:13 +0300 Subject: [PATCH 25/56] Filled not existed translation, minor translation changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fill empty lines. Some translations has been corrected ("вы" required form "нажмите"; project name "Colobot" can not be translated to "КОЛОБОТ"). --- po/ru.po | 120 +++++++++++++++++++++++++++---------------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/po/ru.po b/po/ru.po index 539ed447..746fdb4a 100644 --- a/po/ru.po +++ b/po/ru.po @@ -37,7 +37,7 @@ msgid "..power cell" msgstr "Батарею" msgid "1) First click on the key you want to redefine." -msgstr "1) Сначала нажми на клавишу, которую вы хотите переопределить." +msgstr "1) Сначала нажмите на клавишу, которую вы хотите переопределить." msgid "2) Then press the key you want to use instead." msgstr "2) После этого нажмите на клавишу, которую вы хотите использовать." @@ -61,7 +61,7 @@ msgid "Abort\\Abort the current mission" msgstr "Выход\\Прервать текущую миссию" msgid "Access beyond array limit" -msgstr "Доступ к массиву за предел" +msgstr "Доступ к элементу за пределами массива" msgid "Access to solution\\Shows the solution (detailed instructions for missions)" msgstr "Доступ к решению\\Показывает решение (подробные инструкции для миссий)" @@ -70,7 +70,7 @@ msgid "Access to solutions\\Show program \"4: Solution\" in the exercises" msgstr "Доступ к решению\\Показывает решение \"4: Решение\" в упражнениях" msgid "Add new program" -msgstr "" +msgstr "Добавить новую программу" msgid "Alien Queen" msgstr "Королева чужих" @@ -82,7 +82,7 @@ msgid "Already carrying something" msgstr "Уже что-то несу" msgid "Alternative camera mode\\Move sideways instead of rotating (in free camera)" -msgstr "" +msgstr "Альтернативный режим камеры\\Движение в стороны вместо поворачивания (в режиме свободной камеры)" msgid "Analysis already performed" msgstr "Анализ уже выполнен" @@ -94,7 +94,7 @@ msgid "Analyzes only organic matter" msgstr "Анализирую только органические вещества" msgid "Anisotropy level\\Anisotropy level" -msgstr "" +msgstr "Уровень анизотр. фильтр.\\Уровень анизотропной фильтрации" msgid "Ant" msgstr "Муравей" @@ -121,13 +121,13 @@ msgid "Automatic indent\\When program editing" msgstr "Автоматический отступ\\При редактировании программы" msgid "Autosave interval\\How often your game will autosave" -msgstr "" +msgstr "Интервал автосохр.\\Как часно ваша игра будет сохраняться автоматически" msgid "Autosave slots\\How many autosave slots you'll have" -msgstr "" +msgstr "Кол-во автосохр.\\Как много мест для автосохранений у вас есть" msgid "Autosave\\Enables autosave" -msgstr "" +msgstr "Автосохранение\\Включает автоматическое сохранение" msgid "Back" msgstr "Назад" @@ -152,7 +152,7 @@ msgid "Black box" msgstr "Черный ящик" msgid "Blood\\Display blood when the astronaut is hit" -msgstr "" +msgstr "Кровь\\Показывать кровь когда астронавт ранен" msgid "Blue" msgstr "Синий" @@ -179,7 +179,7 @@ msgid "Build a derrick" msgstr "Построить буровую вышку" msgid "Build a destroyer" -msgstr "" +msgstr "Построить уничтожитель" msgid "Build a exchange post" msgstr "Построить пост по обмену сообщениями" @@ -291,7 +291,7 @@ msgstr "Отдалить камеру\\Перемещение камеры на #, fuzzy msgid "Camera border scrolling\\Scrolling when the mouse touches right or left border" -msgstr "Прокрутка\\Прокрутка, когда указатель мыши касается граней экрана" +msgstr "Прокрутка\\Прокрутка, когда указатель мыши касается правой или левой грани экрана" msgid "Camera closer\\Moves the camera forward" msgstr "Приблизать камеру\\Перемещение камеры вперед" @@ -313,10 +313,10 @@ msgid "Camera up\\Turns the camera up" msgstr "Камера (\\key camera;)" msgid "Can not produce not researched object" -msgstr "" +msgstr "Невозможно изготовить неизученный объект" msgid "Can not produce this object in this mission" -msgstr "" +msgstr "Невозможно изготовить этот объект в этой миссии" msgid "Can't open file" msgstr "Невозможно открыть файл" @@ -346,7 +346,7 @@ msgid "Chapters:" msgstr "Разделы:" msgid "Cheat console\\Show cheat console" -msgstr "" +msgstr "Консоль чит-кодов\\Показать консоль для чит-кодов" msgid "Checkpoint" msgstr "Контрольная точка" @@ -355,7 +355,7 @@ msgid "Climb\\Increases the power of the jet" msgstr "Взлет и подъем\\Увеличивает мощность реактивного двигателя" msgid "Clone program" -msgstr "" +msgstr "Клонировать программу" #, fuzzy msgid "Clone selected program" @@ -371,13 +371,13 @@ msgid "Code battles" msgstr "" msgid "Code battles\\Program your robot to be the best of them all!" -msgstr "" +msgstr "Code battles\\Запрограммируйте собственного робота чтобы быть лучшим среди них!" msgid "Colobot rules!" msgstr "Правила игры!" msgid "Colobot: Gold Edition" -msgstr "" +msgstr "Colobot: Gold Edition" msgid "Command line" msgstr "Командная строка" @@ -413,7 +413,7 @@ msgid "Custom levels:" msgstr "Пользовательские уровни:" msgid "Custom levels\\Levels from mods created by the users" -msgstr "" +msgstr "Пользовательские уровни\\Уровни из модов, созданных пользователями" msgid "Customize your appearance" msgstr "Настроить свой внешний вид" @@ -441,7 +441,7 @@ msgid "Descend\\Reduces the power of the jet" msgstr "Снижение и посадка\\Понижение мощности реактивного двигателя" msgid "Destroy" -msgstr "" +msgstr "Уничтожить" msgid "Destroy the building" msgstr "Уничтожить здание" @@ -478,10 +478,10 @@ msgid "Dynamic lighting\\Mobile light sources" msgstr "Динамическое освещение\\Подвижные источники света" msgid "Dynamic shadows ++\\Dynamic shadows + self shadowing" -msgstr "" +msgstr "Динамические тени\\Динамические тени + самозатенение" msgid "Dynamic shadows\\Beautiful shadows!" -msgstr "" +msgstr "Динамические тени\\Прекрасные тени!" msgid "Edit the selected program" msgstr "Изменить выбранную программу" @@ -517,13 +517,13 @@ msgid "Exercises\\Programming exercises" msgstr "Упражнения\\Упражнения по программированию" msgid "Explode (\\key action;)" -msgstr "" +msgstr "Взорвать (\\key action;)" msgid "Explosive" msgstr "Взрывчатка" msgid "Expression expected after =" -msgstr "" +msgstr "Выражение ожидалось после =" msgid "Extend shield (\\key action;)" msgstr "Поднять щит (\\key action;)" @@ -620,7 +620,7 @@ msgid "Gantry crane" msgstr "Козловой кран" msgid "Generating" -msgstr "" +msgstr "Выработка" msgid "Gold Edition development by:" msgstr "\"Золотое издание\" было разработано:" @@ -647,7 +647,7 @@ msgid "Ground not flat enough" msgstr "Земля недостаточно плоская" msgid "Hair color:" -msgstr "Волосы:" +msgstr "Цвет волос:" msgid "Head\\Face and hair" msgstr "Голова\\Лицо и волосы" @@ -692,7 +692,7 @@ msgid "Inappropriate cell type" msgstr "Батарея не подходит" msgid "Inappropriate object" -msgstr "" +msgstr "Неподходящий объект" msgid "Incorrect index type" msgstr "Неверный тип индекса" @@ -731,10 +731,10 @@ msgid "Instructions\\Shows the instructions for the current mission" msgstr "Инструкции\\Показывает инструкции по текущей миссии" msgid "Internal error - tell the developers" -msgstr "" +msgstr "Внутренняя ошибка - сообщите разработчикам" msgid "Invert\\Invert values on this axis" -msgstr "" +msgstr "Инвертир.\\Инвертировать значения по этой оси" msgid "Jet temperature" msgstr "Температура реактивного двигателя" @@ -794,21 +794,21 @@ msgid "Load\\Loads the selected mission" msgstr "Загрузить\\Загрузить выбранную миссию" msgid "Loading basic level settings" -msgstr "" +msgstr "Загрузка основных настроек уровня" #, fuzzy msgid "Loading finished!" -msgstr "Программа выполнена" +msgstr "Загрузка завершена!" msgid "Loading music" -msgstr "" +msgstr "Загрузка музыки" #, fuzzy msgid "Loading objects" -msgstr "Список объектов" +msgstr "Загрузка объектов" msgid "Loading terrain" -msgstr "" +msgstr "Загрузка местности" msgid "Lowest\\Minimum graphic quality (highest frame rate)" msgstr "Низкое\\Минимальное качество графики (быстро)" @@ -882,7 +882,7 @@ msgid "Next object\\Selects the next object" msgstr "Следующий объект\\Выбор следующего объекта" msgid "No" -msgstr "" +msgstr "Нет" msgid "No energy in the subsoil" msgstr "Под землей нет запасов энергии" @@ -906,7 +906,7 @@ msgid "No more energy" msgstr "Нет энергии" msgid "No ore in the subsoil" -msgstr "" +msgstr "Под поверхностью нет руды" msgid "No power cell" msgstr "Нет батареи" @@ -921,13 +921,13 @@ msgid "No titanium ore to convert" msgstr "Нет титановых руд для преобразования" msgid "No titanium to transform" -msgstr "" +msgstr "Нет титана для преобразования" msgid "No uranium to transform" msgstr "Нет урана для преобразования" msgid "No userlevels installed!" -msgstr "" +msgstr "Не установленны пользовательские уровни!" msgid "Normal size" msgstr "Нормальный размер" @@ -945,7 +945,7 @@ msgid "Not enough energy yet" msgstr "Не хватает энергии" msgid "Not found anything to destroy" -msgstr "" +msgstr "Ничего не найдено для уничтожения" msgid "Nothing to analyze" msgstr "Нечего анализировать" @@ -1032,16 +1032,16 @@ msgid "Paste (Ctrl+V)" msgstr "Вставить (Ctrl+V)" msgid "Pause blur\\Blur the background on the pause screen" -msgstr "" +msgstr "Размытие при паузе\\Размытие фона во время паузы" msgid "Pause in background\\Pause the game when the window is unfocused" -msgstr "" +msgstr "Пауза в фоне\\Приостанавливает игру когда окно не в фокусе" msgid "Pause/continue" msgstr "Пауза/продолжить" msgid "Pause\\Pause the game without opening menu" -msgstr "" +msgstr "Пауза\\Приостанавливает игру без открытия меню" msgid "Phazer shooter" msgstr "Фазовый стрелок" @@ -1125,7 +1125,7 @@ msgid "Private\\Private folder" msgstr "Личное\\Личная папка" msgid "Processing level file" -msgstr "" +msgstr "Обработка файла уровня" #, fuzzy msgid "Program cloned" @@ -1165,7 +1165,7 @@ msgid "Quake at explosions\\The screen shakes at explosions" msgstr "Землетряс. при взрывах\\Тряска экрана при взрывах" msgid "Quit\\Quit Colobot: Gold Edition" -msgstr "" +msgstr "Выход\\Выйти из Colobot: Gold Edition" msgid "Quit\\Quit the current mission or exercise" msgstr "Выход\\Выход из текущей миссии" @@ -1192,7 +1192,7 @@ msgid "Red flag" msgstr "Красный флаг" msgid "Reflections on the buttons \\Shiny buttons" -msgstr "Отражения кнопок \\Блестящие кнопки" +msgstr "Отражения на кнопках \\Блестящие кнопки" msgid "Remains of Apollo mission" msgstr "Остатки миссии Аполлон" @@ -1239,7 +1239,7 @@ msgid "Restoring CBot execution state" msgstr "" msgid "Restoring saved objects" -msgstr "" +msgstr "Восстановить сохранённые объекты" msgid "Return to start" msgstr "Вернуться в начало" @@ -1308,7 +1308,7 @@ msgid "Semicolon terminator missing" msgstr "Отсутствует точка с запятой" msgid "Shadow resolution\\Higher means better range and quality, but slower" -msgstr "" +msgstr "Разрешение тени\\Больше означает более лучшие дистанция и качество, но более медленные" msgid "Shield level" msgstr "Уровень брони" @@ -1457,13 +1457,13 @@ msgid "Target bot" msgstr "Целевой бот" msgid "Terrain relief" -msgstr "" +msgstr "Рельеф местности" msgid "Texture filtering\\Texture filtering" -msgstr "" +msgstr "Фильтрация текстур\\Фильтрация текстур" msgid "Textures" -msgstr "" +msgstr "Текстуры" msgid "The expression must return a boolean value" msgstr "Выражение должно возвращать логическое значение" @@ -1484,7 +1484,7 @@ msgid "This class does not exist" msgstr "Этот класс не существует" msgid "This is example code that cannot be run directly" -msgstr "" +msgstr "Это пример кода который не может быть непосредственно запущен" msgid "This is not a member of this class" msgstr "Это не член этого класса" @@ -1493,13 +1493,13 @@ msgid "This label does not exist" msgstr "Эта метка не существует" msgid "This menu is for userlevels from mods, but you didn't install any" -msgstr "" +msgstr "Это меню для пользовательских уровней из модов, но вы ни одного не уставили" msgid "This object is not a member of a class" msgstr "Этот объект не член класса" msgid "This program is read-only, clone it to edit" -msgstr "" +msgstr "Эта программа только для чтения, для редактирования клонируйте её" msgid "Thump (\\key action;)" msgstr "Удар (\\key action;)" @@ -1529,13 +1529,13 @@ msgid "Too close to a building" msgstr "Слишком близко к зданию" msgid "Too close to an existing flag" -msgstr "" +msgstr "Слишком близко к существующему флагу" msgid "Too close to space ship" msgstr "Слишком близко к кораблю" msgid "Too many flags of this color (maximum 5)" -msgstr "" +msgstr "Слишком много флагов этого цвета (максимум 5)" msgid "Too many parameters" msgstr "Слишком много параметров" @@ -1577,7 +1577,7 @@ msgid "Type declaration missing" msgstr "Не задан тип" msgid "Unable to control enemy objects" -msgstr "" +msgstr "Невозможно контролировать вражеские объекты" msgid "Undo (Ctrl+Z)" msgstr "Отмена (Ctrl+Z)" @@ -1679,7 +1679,7 @@ msgid "Yellow flag" msgstr "Желтый флаг" msgid "Yes" -msgstr "" +msgstr "Да" msgid "You can fly with the keys (\\key gup;) and (\\key gdown;)" msgstr "Вы можете лететь с помощью клавиш (\\key gup;) и (\\key gdown;)" @@ -1761,7 +1761,7 @@ msgid "\\Red flags" msgstr "\\Красный флаг" msgid "\\Return to Colobot: Gold Edition" -msgstr "" +msgstr "Вернуться к Colobot: Gold Edition" msgid "\\SatCom on standby" msgstr "\\SatCom ждет" @@ -1794,7 +1794,7 @@ msgid "\\Use the orange pencil" msgstr "\\Использовать оранжевое перо" msgid "\\Use the purple pencil" -msgstr "" +msgstr "Использовать фиолетовое перо" msgid "\\Use the red pencil" msgstr "\\Использовать красное перо" @@ -1833,7 +1833,7 @@ msgstr "epsitec.com" #~ msgstr "Здание слишком близко" #~ msgid "COLOBOT" -#~ msgstr "КОЛОБОТ" +#~ msgstr "COLOBOT" #~ msgid "Camera awayest" #~ msgstr "Отдалить камеру" @@ -1873,7 +1873,7 @@ msgstr "epsitec.com" #, fuzzy #~ msgid "Do you want to quit Colobot: Gold Edition?" -#~ msgstr "Вы хотите закрыть COLOBOT?" +#~ msgstr "Вы хотите закрыть Colobot: Gold Edition?" #~ msgid "Exit film\\Film at the exit of exercises" #~ msgstr "Ролик при выходе\\Ролик во время выхода из упражнения" From 191ca3c4ebd710965801103a59e2b9643ae25c35 Mon Sep 17 00:00:00 2001 From: aleksei-sh Date: Tue, 19 Jul 2016 13:13:20 +0300 Subject: [PATCH 26/56] Reducing the length of the lines The length of lines is reduced so that they fit in the interface --- po/ru.po | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/po/ru.po b/po/ru.po index 746fdb4a..2360d6e9 100644 --- a/po/ru.po +++ b/po/ru.po @@ -413,7 +413,7 @@ msgid "Custom levels:" msgstr "Пользовательские уровни:" msgid "Custom levels\\Levels from mods created by the users" -msgstr "Пользовательские уровни\\Уровни из модов, созданных пользователями" +msgstr "Пользоват. уровни\\Уровни из модов, созданных пользователями" msgid "Customize your appearance" msgstr "Настроить свой внешний вид" @@ -475,10 +475,10 @@ msgid "Dust\\Dust and dirt on bots and buildings" msgstr "Пыль\\Пыль и грязь на ботах и зданиях" msgid "Dynamic lighting\\Mobile light sources" -msgstr "Динамическое освещение\\Подвижные источники света" +msgstr "Динамич. освещение\\Подвижные источники света" msgid "Dynamic shadows ++\\Dynamic shadows + self shadowing" -msgstr "Динамические тени\\Динамические тени + самозатенение" +msgstr "Динамич. тени ++\\Динамические тени + самозатенение" msgid "Dynamic shadows\\Beautiful shadows!" msgstr "Динамические тени\\Прекрасные тени!" From 4cb2eb6d377425fe3a2f63da1dae70d1b520b79e Mon Sep 17 00:00:00 2001 From: krzys-h Date: Tue, 9 Aug 2016 19:07:50 +0200 Subject: [PATCH 27/56] Removed fuzzy markers for lines fixed in #805 --- po/ru.po | 4 ---- 1 file changed, 4 deletions(-) diff --git a/po/ru.po b/po/ru.po index 2360d6e9..2e375df6 100644 --- a/po/ru.po +++ b/po/ru.po @@ -289,7 +289,6 @@ msgstr "Камера (\\key camera;)" msgid "Camera back\\Moves the camera backward" msgstr "Отдалить камеру\\Перемещение камеры назад" -#, fuzzy msgid "Camera border scrolling\\Scrolling when the mouse touches right or left border" msgstr "Прокрутка\\Прокрутка, когда указатель мыши касается правой или левой грани экрана" @@ -796,14 +795,12 @@ msgstr "Загрузить\\Загрузить выбранную миссию" msgid "Loading basic level settings" msgstr "Загрузка основных настроек уровня" -#, fuzzy msgid "Loading finished!" msgstr "Загрузка завершена!" msgid "Loading music" msgstr "Загрузка музыки" -#, fuzzy msgid "Loading objects" msgstr "Загрузка объектов" @@ -1871,7 +1868,6 @@ msgstr "epsitec.com" #~ msgid "Developed by :" #~ msgstr "Разработка :" -#, fuzzy #~ msgid "Do you want to quit Colobot: Gold Edition?" #~ msgstr "Вы хотите закрыть Colobot: Gold Edition?" From 71aa7e468bcca287b1bbe6cb97e242626c2607d7 Mon Sep 17 00:00:00 2001 From: melex750 Date: Tue, 9 Aug 2016 14:55:56 -0400 Subject: [PATCH 28/56] Add unit tests for circular reference --- test/unit/CBot/CBot_test.cpp | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index 2d96d59e..7158bf06 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -1006,6 +1006,41 @@ TEST_F(CBotUT, ClassStringAdd_Issue535) ); } +TEST_F(CBotUT, ClassCompileCircularReference_Issue433) +{ + ExecuteTest( + "public class OtherClass {\n" + " TestClass testclass;\n" + "}\n" + "public class TestClass {\n" + " int test;\n" + " OtherClass otherclass;\n" + "}\n" + "extern void TestCompileCircularReference()\n" + "{\n" + " TestClass t();\n" + "}\n" + ); +} + +TEST_F(CBotUT, ClassTestClassDefinedAfterReference) +{ + ExecuteTest( + "public class OtherClass {\n" + " TestClass testclass = new TestClass();\n" + "}\n" + "public class TestClass {\n" + " int test = 246;\n" + "}\n" + "extern void TestDefinedAfterReference()\n" + "{\n" + " OtherClass o();\n" + " TestClass t = o.testclass;\n" + " ASSERT(t.test == 246);\n" + "}\n" + ); +} + TEST_F(CBotUT, String) { ExecuteTest( From 88227a3647e88508d817af236b81aeb4d5e522d0 Mon Sep 17 00:00:00 2001 From: Jeremy Mickelson Date: Sat, 13 Aug 2016 12:13:41 -0600 Subject: [PATCH 29/56] Allow multiple objects with exactly the same distance to be returned by `radarall` --- src/object/object_manager.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/object/object_manager.cpp b/src/object/object_manager.cpp index 9c1126c7..0e6e28a3 100644 --- a/src/object/object_manager.cpp +++ b/src/object/object_manager.cpp @@ -282,7 +282,8 @@ std::vector CObjectManager::RadarAll(CObject* pThis, Math::Vector this RadarFilter filter_flying = static_cast(filter & (FILTER_ONLYLANDING | FILTER_ONLYFLYING)); RadarFilter filter_enemy = static_cast(filter & (FILTER_FRIENDLY | FILTER_ENEMY | FILTER_NEUTRAL)); - std::map best; + std::multimap best; + for ( auto it = m_objects.begin() ; it != m_objects.end() ; ++it ) { pObj = it->second.get(); @@ -357,7 +358,7 @@ std::vector CObjectManager::RadarAll(CObject* pThis, Math::Vector this a = Math::RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW ! if ( Math::TestAngle(a, iAngle-focus/2.0f, iAngle+focus/2.0f) || focus >= Math::PI*2.0f ) { - best[d] = pObj; + best.insert(std::make_pair(d, pObj)); } } From 859cec774a85e45141be3d32fcf788709cd8d13d Mon Sep 17 00:00:00 2001 From: Jeremy Mickelson Date: Sat, 13 Aug 2016 12:18:54 -0600 Subject: [PATCH 30/56] Added comment explaining container choice --- src/object/object_manager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/object/object_manager.cpp b/src/object/object_manager.cpp index 0e6e28a3..a903a3a4 100644 --- a/src/object/object_manager.cpp +++ b/src/object/object_manager.cpp @@ -282,6 +282,8 @@ std::vector CObjectManager::RadarAll(CObject* pThis, Math::Vector this RadarFilter filter_flying = static_cast(filter & (FILTER_ONLYLANDING | FILTER_ONLYFLYING)); RadarFilter filter_enemy = static_cast(filter & (FILTER_FRIENDLY | FILTER_ENEMY | FILTER_NEUTRAL)); + // Use a multimap to allow for multiple objects at exactly the same distance + // from the origin to be returned. std::multimap best; for ( auto it = m_objects.begin() ; it != m_objects.end() ; ++it ) From 3146d4ef358bc06767013993ccb18487a639eb65 Mon Sep 17 00:00:00 2001 From: melex750 Date: Sun, 14 Aug 2016 16:56:17 -0400 Subject: [PATCH 31/56] Add method chaining for class constructor calls --- src/CBot/CBotInstr/CBotDefClass.cpp | 43 +++++++++++++++++++++++++ src/CBot/CBotInstr/CBotDefClass.h | 3 ++ src/CBot/CBotInstr/CBotExprRetVar.cpp | 17 ++++++---- src/CBot/CBotInstr/CBotExprRetVar.h | 2 +- src/CBot/CBotInstr/CBotInstrMethode.cpp | 7 ++-- src/CBot/CBotInstr/CBotInstrMethode.h | 3 +- src/CBot/CBotInstr/CBotNew.cpp | 43 ++++++++++++++++++++++++- src/CBot/CBotInstr/CBotNew.h | 3 ++ test/unit/CBot/CBot_test.cpp | 35 ++++++++++++++++++++ 9 files changed, 143 insertions(+), 13 deletions(-) diff --git a/src/CBot/CBotInstr/CBotDefClass.cpp b/src/CBot/CBotInstr/CBotDefClass.cpp index bfd0dedf..cd73d9cc 100644 --- a/src/CBot/CBotInstr/CBotDefClass.cpp +++ b/src/CBot/CBotInstr/CBotDefClass.cpp @@ -19,6 +19,7 @@ #include "CBot/CBotInstr/CBotDefClass.h" +#include "CBot/CBotInstr/CBotExprRetVar.h" #include "CBot/CBotInstr/CBotInstrUtils.h" #include "CBot/CBotInstr/CBotLeftExprVar.h" @@ -44,6 +45,7 @@ CBotDefClass::CBotDefClass() m_expr = nullptr; m_hasParams = false; m_nMethodeIdent = 0; + m_exprRetVar = nullptr; } //////////////////////////////////////////////////////////////////////////////// @@ -150,6 +152,16 @@ CBotInstr* CBotDefClass::Compile(CBotToken* &p, CBotCStack* pStack, CBotClass* p goto error; } + pStk->SetCopyVar(var); + // chained method ? + if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStk, true))) + { + inst->m_exprRetVar->SetToken(vartoken); + delete pStk->TokenStack(); + } + pStk->SetVar(nullptr); + + if ( !pStk->IsOk() ) goto error; } if (IsOfType(p, ID_ASS)) // with a assignment? @@ -232,6 +244,19 @@ bool CBotDefClass::Execute(CBotStack* &pj) CBotStack* pile = pj->AddStack(this);//essential for SetState() // if ( pile == EOX ) return true; + if (m_exprRetVar != nullptr) // Class c().method(); + { + if (pile->IfStep()) return false; + if (pile->GetState() == 4) + { + CBotStack* pile3 = pile->AddStack(); + if (!m_exprRetVar->Execute(pile3)) return false; + pile3->SetVar(nullptr); + pile->Return(pile3); // release pile3 stack + pile->SetState(5); + } + } + CBotToken* pt = &m_token; CBotClass* pClass = CBotClass::Find(pt); @@ -363,6 +388,14 @@ bool CBotDefClass::Execute(CBotStack* &pj) pile->SetState(3); // finished this part } + if (m_exprRetVar != nullptr && pile->GetState() == 3) // Class c().method(); + { + CBotStack* pile3 = pile->AddStack(); + pile3->SetCopyVar(pThis); + pile->SetState(4); + return false; // go back to the top ^^^ + } + if ( pile->IfStep() ) return false; if ( m_next2b != nullptr && @@ -387,6 +420,16 @@ void CBotDefClass::RestoreState(CBotStack* &pj, bool bMain) pThis->SetUniqNum((static_cast(m_var))->m_nIdent); // its attribute a unique number } + if (m_exprRetVar != nullptr) // Class c().method(); + { + if (pile->GetState() == 4) + { + CBotStack* pile3 = pile->RestoreStack(); + m_exprRetVar->RestoreState(pile3, bMain); + return; + } + } + CBotToken* pt = &m_token; CBotClass* pClass = CBotClass::Find(pt); bool bIntrincic = pClass->IsIntrinsic(); diff --git a/src/CBot/CBotInstr/CBotDefClass.h b/src/CBot/CBotInstr/CBotDefClass.h index 82c59528..abad7952 100644 --- a/src/CBot/CBotInstr/CBotDefClass.h +++ b/src/CBot/CBotInstr/CBotDefClass.h @@ -85,6 +85,9 @@ private: //! Constructor method unique identifier long m_nMethodeIdent; + //! Instruction to chain method calls after constructor + CBotInstr* m_exprRetVar; + }; } // namespace CBot diff --git a/src/CBot/CBotInstr/CBotExprRetVar.cpp b/src/CBot/CBotInstr/CBotExprRetVar.cpp index 9a1eeb72..96ff03c0 100644 --- a/src/CBot/CBotInstr/CBotExprRetVar.cpp +++ b/src/CBot/CBotInstr/CBotExprRetVar.cpp @@ -41,17 +41,13 @@ CBotExprRetVar::~CBotExprRetVar() } //////////////////////////////////////////////////////////////////////////////// -CBotInstr* CBotExprRetVar::Compile(CBotToken*& p, CBotCStack* pStack) +CBotInstr* CBotExprRetVar::Compile(CBotToken*& p, CBotCStack* pStack, bool bMethodsOnly) { if (p->GetType() == ID_DOT) { CBotVar* var = pStack->GetVar(); - if (var == nullptr) - { - pStack->SetError(CBotErrNoTerminator, p->GetStart()); - return nullptr; - } + if (var == nullptr) return nullptr; CBotCStack* pStk = pStack->TokenStack(); CBotInstr* inst = new CBotExprRetVar(); @@ -61,6 +57,8 @@ CBotInstr* CBotExprRetVar::Compile(CBotToken*& p, CBotCStack* pStack) pStk->SetStartError(p->GetStart()); if (var->GetType() == CBotTypArrayPointer) { + if (bMethodsOnly) goto err; + if (IsOfType( p, ID_OPBRK )) { CBotIndexExpr* i = new CBotIndexExpr(); @@ -92,11 +90,16 @@ CBotInstr* CBotExprRetVar::Compile(CBotToken*& p, CBotCStack* pStack) { if (p->GetNext()->GetType() == ID_OPENPAR) { - CBotInstr* i = CBotInstrMethode::Compile(p, pStk, var); + CBotInstr* i = CBotInstrMethode::Compile(p, pStk, var, bMethodsOnly); if (!pStk->IsOk()) goto err; inst->AddNext3(i); return pStack->Return(inst, pStk); } + else if (bMethodsOnly) + { + p = p->GetPrev(); + goto err; + } else { CBotFieldExpr* i = new CBotFieldExpr(); diff --git a/src/CBot/CBotInstr/CBotExprRetVar.h b/src/CBot/CBotInstr/CBotExprRetVar.h index a1c37f55..2c8321e7 100644 --- a/src/CBot/CBotInstr/CBotExprRetVar.h +++ b/src/CBot/CBotInstr/CBotExprRetVar.h @@ -36,7 +36,7 @@ public: CBotExprRetVar(); ~CBotExprRetVar(); - static CBotInstr* Compile(CBotToken*& p, CBotCStack* pStack); + static CBotInstr* Compile(CBotToken*& p, CBotCStack* pStack, bool bMethodsOnly = false); /*! * \brief Execute diff --git a/src/CBot/CBotInstr/CBotInstrMethode.cpp b/src/CBot/CBotInstr/CBotInstrMethode.cpp index cbc3a608..bf4ec41e 100644 --- a/src/CBot/CBotInstr/CBotInstrMethode.cpp +++ b/src/CBot/CBotInstr/CBotInstrMethode.cpp @@ -46,7 +46,7 @@ CBotInstrMethode::~CBotInstrMethode() } //////////////////////////////////////////////////////////////////////////////// -CBotInstr* CBotInstrMethode::Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* var) +CBotInstr* CBotInstrMethode::Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* var, bool bMethodChain) { CBotInstrMethode* inst = new CBotInstrMethode(); inst->SetToken(p); // corresponding token @@ -90,9 +90,10 @@ CBotInstr* CBotInstrMethode::Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* } else pStack->SetVar(nullptr); - if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStack))) + pp = p; + if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStack, bMethodChain))) { - inst->m_exprRetVar->SetToken(&inst->m_token); + inst->m_exprRetVar->SetToken(pp); delete pStack->TokenStack(); } diff --git a/src/CBot/CBotInstr/CBotInstrMethode.h b/src/CBot/CBotInstr/CBotInstrMethode.h index a74a49a4..502dcf4e 100644 --- a/src/CBot/CBotInstr/CBotInstrMethode.h +++ b/src/CBot/CBotInstr/CBotInstrMethode.h @@ -38,9 +38,10 @@ public: * \param p * \param pStack * \param pVar + * \param bMethodChain If true, allows chaining methods only * \return */ - static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* pVar); + static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* pVar, bool bMethodChain = false); /*! * \brief Execute diff --git a/src/CBot/CBotInstr/CBotNew.cpp b/src/CBot/CBotInstr/CBotNew.cpp index a302b394..e7d973c9 100644 --- a/src/CBot/CBotInstr/CBotNew.cpp +++ b/src/CBot/CBotInstr/CBotNew.cpp @@ -24,6 +24,7 @@ #include "CBot/CBotCStack.h" #include "CBot/CBotClass.h" +#include "CBot/CBotInstr/CBotExprRetVar.h" #include "CBot/CBotInstr/CBotInstrUtils.h" #include "CBot/CBotVar/CBotVar.h" @@ -105,7 +106,17 @@ CBotInstr* CBotNew::Compile(CBotToken* &p, CBotCStack* pStack) // makes pointer to the object on the stack pStk->SetVar(pVar); - return pStack->Return(inst, pStk); + + pp = p; + // chained method ? + if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStk, true))) + { + inst->m_exprRetVar->SetToken(pp); + delete pStk->TokenStack(); + } + + if (pStack->IsOk()) + return pStack->Return(inst, pStk); } error: delete inst; @@ -117,6 +128,16 @@ bool CBotNew::Execute(CBotStack* &pj) { CBotStack* pile = pj->AddStack(this); //main stack + if (m_exprRetVar != nullptr) // new Class().method() + { + if (pile->GetState() == 2) + { + CBotStack* pile3 = pile->AddStack(); + if (!m_exprRetVar->Execute(pile3)) return false; + return pj->Return(pile3); + } + } + if (pile->IfStep()) return false; CBotStack* pile1 = pj->AddStack2(); //secondary stack @@ -186,6 +207,16 @@ bool CBotNew::Execute(CBotStack* &pj) pThis->ConstructorSet(); // indicates that the constructor has been called } + if (m_exprRetVar != nullptr) // new Class().method() + { + pile->AddStack()->Delete(); // release pile2 stack + CBotStack* pile3 = pile->AddStack(); // add new stack + pile3->SetCopyVar(pThis); // copy the pointer (from pile1) + pile1->Delete(); // release secondary stack(pile1) + pile->SetState(2); + return false; // go back to the top ^^^ + } + return pj->Return(pile1); // passes below } @@ -197,6 +228,16 @@ void CBotNew::RestoreState(CBotStack* &pj, bool bMain) CBotStack* pile = pj->RestoreStack(this); //primary stack if (pile == nullptr) return; + if (m_exprRetVar != nullptr) // new Class().method() + { + if (pile->GetState() == 2) + { + CBotStack* pile3 = pile->RestoreStack(); + m_exprRetVar->RestoreState(pile3, bMain); + return; + } + } + CBotStack* pile1 = pj->AddStack2(); //secondary stack CBotToken* pt = &m_vartoken; diff --git a/src/CBot/CBotInstr/CBotNew.h b/src/CBot/CBotInstr/CBotNew.h index 76cf3e92..c69da3aa 100644 --- a/src/CBot/CBotInstr/CBotNew.h +++ b/src/CBot/CBotInstr/CBotNew.h @@ -66,6 +66,9 @@ private: long m_nMethodeIdent; CBotToken m_vartoken; + //! Instruction to chain method calls after constructor + CBotInstr* m_exprRetVar; + }; } // namespace CBot diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index da9fd95b..071abb94 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -1751,3 +1751,38 @@ TEST_F(CBotUT, InstrCallAccessMemberNewObjectDestructor) "}\n" ); } + +TEST_F(CBotUT, ClassConstructorMethodChain) +{ + ExecuteTest( + "public class TestClass {\n" + " int a = 123;\n" + " int b = 246;\n" + " TestClass testSetA(int x) { a = x; return this; }\n" + " TestClass testSetB(int y) { b = y; return this; }\n" + "}\n" + "extern void ConstructorMethodChain() {\n" + " TestClass tc().testSetA(111).testSetB(222);\n" + " ASSERT(tc.a == 111);\n" + " ASSERT(tc.b == 222);\n" + "}\n" + ); +} + +TEST_F(CBotUT, ClassNewConstructorMethodChain) +{ + ExecuteTest( + "public class TestClass {\n" + " int a = 123;\n" + " int b = 246;\n" + " TestClass testSetA(int x) { a = x; return this; }\n" + " TestClass testSetB(int y) { b = y; return this; }\n" + "}\n" + "extern void NewConstructorMethodChain() {\n" + " TestClass tc;\n" + " tc = new TestClass().testSetA(111).testSetB(222);\n" + " ASSERT(tc.a == 111);\n" + " ASSERT(tc.b == 222);\n" + "}\n" + ); +} From 584d782a9ff4cb2ee65ecec738a53e52429370b9 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Mon, 15 Aug 2016 18:08:18 +0200 Subject: [PATCH 32/56] Add VS Code files to .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 65c0087a..f9f69301 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ Makefile # Ignore QtCreator temp files CMakeLists.txt.user CMakeLists.txt.user.* + +# Ignore Visual Studio Code files +/.vscode From 92b1e544ecb0220af88ad896e4e3abee4ef9c9c1 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Thu, 18 Aug 2016 10:02:54 +0200 Subject: [PATCH 33/56] Remove default lambda parameters They cause compilation error on MSVC and should give errors, as it's explicitly stated in the C++11 standard: http://stackoverflow.com/questions/6281472/why-does-a-default-argument-for-a-lambda-argument-trigger-a-pedantic-gcc-warni --- src/graphics/engine/engine.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp index 7e5d4a80..5c4a317a 100644 --- a/src/graphics/engine/engine.cpp +++ b/src/graphics/engine/engine.cpp @@ -5057,7 +5057,7 @@ void CEngine::DrawStats() SetState(ENG_RSTATE_TEXT); - auto drawStatsLine = [&](const std::string& name = "", const std::string& value = "", const std::string& value2 = "") + auto drawStatsLine = [&](const std::string& name, const std::string& value, const std::string& value2) { if (!name.empty()) m_text->DrawText(name+":", FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f)); @@ -5103,14 +5103,14 @@ void CEngine::DrawStats() CProfiler::GetPerformanceCounterTime(PCNT_RENDER_SHADOW_MAP); drawStatsCounter("Event processing", PCNT_EVENT_PROCESSING); - drawStatsLine( ""); + drawStatsLine( "", "", ""); drawStatsCounter("Frame update", PCNT_UPDATE_ALL); drawStatsValue (" Engine update", engineUpdate); drawStatsCounter(" Particle update", PCNT_UPDATE_PARTICLE); drawStatsValue (" Game update", gameUpdate); drawStatsCounter(" CBot programs", PCNT_UPDATE_CBOT); drawStatsValue( " Other update", otherUpdate); - drawStatsLine( ""); + drawStatsLine( "", "", ""); drawStatsCounter("Frame render", PCNT_RENDER_ALL); drawStatsCounter(" Particle render", PCNT_RENDER_PARTICLE_WORLD); drawStatsCounter(" Water render", PCNT_RENDER_WATER); @@ -5121,13 +5121,13 @@ void CEngine::DrawStats() drawStatsCounter(" Shadow map render", PCNT_RENDER_SHADOW_MAP); drawStatsValue( " Other render", otherRender); drawStatsCounter("Swap buffers & VSync", PCNT_SWAP_BUFFERS); - drawStatsLine( ""); - drawStatsLine( "Triangles", StrUtils::ToString(m_statisticTriangle)); - drawStatsLine( "FPS", StrUtils::Format("%.3f", m_fps)); - drawStatsLine( ""); + drawStatsLine( "", "", ""); + drawStatsLine( "Triangles", StrUtils::ToString(m_statisticTriangle), ""); + drawStatsLine( "FPS", StrUtils::Format("%.3f", m_fps), ""); + drawStatsLine( "", "", ""); std::stringstream str; str << std::fixed << std::setprecision(2) << m_statisticPos.x << "; " << m_statisticPos.z; - drawStatsLine( "Position", str.str()); + drawStatsLine( "Position", str.str(), ""); } void CEngine::DrawTimer() From 974b6efad5fff67d4fd585c02599477fc58c6b8f Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Thu, 18 Aug 2016 12:59:29 +0200 Subject: [PATCH 34/56] Add -pedantic-errors flag to C++ compilers --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 771cc482..48aebbcb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,7 +132,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") message(STATUS "Detected GCC version 4.7+") - set(NORMAL_CXX_FLAGS "-std=gnu++11 -Wall -Wold-style-cast") + set(NORMAL_CXX_FLAGS "-std=gnu++11 -Wall -Wold-style-cast -pedantic-errors") set(RELEASE_CXX_FLAGS "-O2") set(DEBUG_CXX_FLAGS "-g -O0") set(TEST_CXX_FLAGS "-pthread") @@ -144,7 +144,7 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") message(STATUS "Detected Clang version 3.1+") - set(NORMAL_CXX_FLAGS "-std=c++11 -Wall -Wold-style-cast") + set(NORMAL_CXX_FLAGS "-std=c++11 -Wall -Wold-style-cast -pedantic-errors") set(RELEASE_CXX_FLAGS "-O2") set(DEBUG_CXX_FLAGS "-g -O0") set(TEST_CXX_FLAGS "-pthread") From 514829de60e8d1de232c2995451c53c5ac7785a9 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Sat, 20 Aug 2016 13:22:53 +0200 Subject: [PATCH 35/56] Normalize dir parameter when copying to clipboard --- src/level/robotmain.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index 7ed6ef21..23c9c9db 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -981,10 +981,13 @@ bool CRobotMain::ProcessEvent(Event &event) { CLevelParserLine line("CreateObject"); line.AddParam("type", MakeUnique(obj->GetType())); + Math::Vector pos = obj->GetPosition()/g_unit; pos.y = 0.0f; line.AddParam("pos", MakeUnique(pos)); - line.AddParam("dir", MakeUnique(obj->GetRotationY())); + + float dir = Math::NormAngle(obj->GetRotationY()) / Math::PI; + line.AddParam("dir", MakeUnique(dir)); std::stringstream ss; ss << line; From e085b4b4223eacf6d1fd4a9bd1163cfc4ff4eb27 Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 24 Aug 2016 13:34:51 +0200 Subject: [PATCH 36/56] Enable C++ exceptions in MSVC Because warnings. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 48aebbcb..7d739cbe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,7 +152,7 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") message(STATUS "Detected MSVC compiler") - set(NORMAL_CXX_FLAGS "/wd\"4244\" /wd\"4309\" /wd\"4800\" /wd\"4996\" /wd\"4351\"") # disable some useless warnings + set(NORMAL_CXX_FLAGS "/wd\"4244\" /wd\"4309\" /wd\"4800\" /wd\"4996\" /wd\"4351\" /EHsc") # disable some useless warnings set(RELEASE_CXX_FLAGS "") set(DEBUG_CXX_FLAGS "") set(TEST_CXX_FLAGS "") From e3b3d7c9aaa60535c63ac75ed0620793b69fd533 Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 24 Aug 2016 18:18:33 +0200 Subject: [PATCH 37/56] Enable debug info for MSVC --- CMakeLists.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d739cbe..29865105 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,10 +153,14 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") message(STATUS "Detected MSVC compiler") set(NORMAL_CXX_FLAGS "/wd\"4244\" /wd\"4309\" /wd\"4800\" /wd\"4996\" /wd\"4351\" /EHsc") # disable some useless warnings - set(RELEASE_CXX_FLAGS "") - set(DEBUG_CXX_FLAGS "") + set(RELEASE_CXX_FLAGS "/MD") + set(DEBUG_CXX_FLAGS "/MDd /ZI") set(TEST_CXX_FLAGS "") add_definitions(-DNOEXCEPT= -DHAS_MSVC_EXCEPTION_BUG) + + # Needed for Debug information (it's set to "No" by default for some reason) + set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG") + set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "/DEBUG") else() message(FATAL_ERROR "Your C++ compiler doesn't seem to be supported.") endif() From ade8566d06ab9eb897f13aa3d0fb33524cc29b2c Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 10 Sep 2016 17:51:52 +0200 Subject: [PATCH 38/56] Update data submodule --- data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data b/data index faebc8f4..7ac99fbd 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit faebc8f4bc20121328b993dd388a2a14617c1fbd +Subproject commit 7ac99fbd72d38c1861a366b3d8a25a3ae7b2443b From 6b6eca61cb56f5859ce0b964b6df7b2139dac12c Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 13 Sep 2016 10:15:28 +0200 Subject: [PATCH 39/56] Update data submodule --- data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data b/data index 7ac99fbd..2b7248ab 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 7ac99fbd72d38c1861a366b3d8a25a3ae7b2443b +Subproject commit 2b7248ab617474f7dd52d05c9a7dfec7ccad6e30 From 4a14a44f3f07ee103161fbec2bcaa4bf5e6e2a84 Mon Sep 17 00:00:00 2001 From: melex750 Date: Sat, 17 Sep 2016 07:58:39 -0400 Subject: [PATCH 40/56] Add implicit cast and null for passing arguments --- src/CBot/CBotEnums.h | 1 + src/CBot/CBotInstr/CBotFunction.cpp | 107 ++++++++++++++++---------- src/CBot/CBotInstr/CBotInstrUtils.cpp | 10 ++- src/common/restext.cpp | 1 + test/unit/CBot/CBot_test.cpp | 88 +++++++++++++++++++++ 5 files changed, 165 insertions(+), 42 deletions(-) diff --git a/src/CBot/CBotEnums.h b/src/CBot/CBotEnums.h index 24e0095b..da0dbea7 100644 --- a/src/CBot/CBotEnums.h +++ b/src/CBot/CBotEnums.h @@ -236,6 +236,7 @@ enum CBotError : int CBotErrPrivate = 5041, //!< protected item CBotErrNoPublic = 5042, //!< missing word "public" CBotErrNoExpression = 5043, //!< expression expected after = + CBotErrAmbiguousCall = 5044, //!< ambiguous call to overloaded function // Runtime errors CBotErrZeroDiv = 6000, //!< division by zero diff --git a/src/CBot/CBotInstr/CBotFunction.cpp b/src/CBot/CBotInstr/CBotFunction.cpp index d00e8ea6..1e7e0b94 100644 --- a/src/CBot/CBotInstr/CBotFunction.cpp +++ b/src/CBot/CBotInstr/CBotFunction.cpp @@ -466,8 +466,7 @@ CBotFunction* CBotFunction::FindLocalOrPublic(long& nIdent, const std::string& n if ( name.empty() ) return nullptr; - int delta = 99999; // seeks the lowest signature - CBotFunction* pFunc = nullptr; // the best function found + std::map funcMap; if ( this != nullptr ) { @@ -482,44 +481,48 @@ CBotFunction* CBotFunction::FindLocalOrPublic(long& nIdent, const std::string& n CBotVar* pw = ppVars[i++]; // provided list parameter while ( pv != nullptr && pw != nullptr) { - if (!TypesCompatibles(pv->GetTypResult(), pw->GetTypResult())) + CBotTypResult paramType = pv->GetTypResult(); + CBotTypResult argType = pw->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); + + if (!TypesCompatibles(paramType, argType)) { - if ( pFunc == nullptr ) TypeOrError = CBotErrBadParam; + if ( funcMap.empty() ) TypeOrError.SetType(CBotErrBadParam); break; } - int d = pv->GetType() - pw->GetType(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); - alpha += d>0 ? d : -10*d; // quality loss, 10 times more expensive! + if (paramType.Eq(CBotTypPointer) && !argType.Eq(CBotTypNullPointer)) + { + CBotClass* c1 = paramType.GetClass(); + CBotClass* c2 = argType.GetClass(); + while (c2 != c1 && c2 != nullptr) // implicit cast + { + alpha += 10; + c2 = c2->GetParent(); + } + } + else + { + int d = pv->GetType() - pw->GetType(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); + alpha += d>0 ? d : -10*d; // quality loss, 10 times more expensive! + } pv = pv->GetNext(); pw = ppVars[i++]; } if ( pw != nullptr ) { - if ( pFunc != nullptr ) continue; + if ( !funcMap.empty() ) continue; if ( TypeOrError.Eq(CBotErrLowParam) ) TypeOrError.SetType(CBotErrNbParam); if ( TypeOrError.Eq(CBotErrUndefCall)) TypeOrError.SetType(CBotErrOverParam); continue; // too many parameters } if ( pv != nullptr ) { - if ( pFunc != nullptr ) continue; + if ( !funcMap.empty() ) continue; if ( TypeOrError.Eq(CBotErrOverParam) ) TypeOrError.SetType(CBotErrNbParam); if ( TypeOrError.Eq(CBotErrUndefCall) ) TypeOrError.SetType(CBotErrLowParam); continue; // not enough parameters } - - if (alpha == 0) // perfect signature - { - nIdent = pt->m_nFuncIdent; - TypeOrError = pt->m_retTyp; - return pt; - } - - if ( alpha < delta ) // a better signature? - { - pFunc = pt; - delta = alpha; - } + funcMap.insert( std::pair(pt, alpha) ); } } } @@ -537,50 +540,72 @@ CBotFunction* CBotFunction::FindLocalOrPublic(long& nIdent, const std::string& n CBotVar* pw = ppVars[i++]; // list of provided parameters while ( pv != nullptr && pw != nullptr) { - if (!TypesCompatibles(pv->GetTypResult(), pw->GetTypResult())) + CBotTypResult paramType = pv->GetTypResult(); + CBotTypResult argType = pw->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); + + if (!TypesCompatibles(paramType, argType)) { - if ( pFunc == nullptr ) TypeOrError = CBotErrBadParam; + if ( funcMap.empty() ) TypeOrError.SetType(CBotErrBadParam); break; } - int d = pv->GetType() - pw->GetType(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); - alpha += d>0 ? d : -10*d; // quality loss, 10 times more expensive! + if (paramType.Eq(CBotTypPointer) && !argType.Eq(CBotTypNullPointer)) + { + CBotClass* c1 = paramType.GetClass(); + CBotClass* c2 = argType.GetClass(); + while (c2 != c1 && c2 != nullptr) // implicit cast + { + alpha += 10; + c2 = c2->GetParent(); + } + } + else + { + int d = pv->GetType() - pw->GetType(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); + alpha += d>0 ? d : -10*d; // quality loss, 10 times more expensive! + } pv = pv->GetNext(); pw = ppVars[i++]; } if ( pw != nullptr ) { - if ( pFunc != nullptr ) continue; + if ( !funcMap.empty() ) continue; // previous useable function if ( TypeOrError.Eq(CBotErrLowParam) ) TypeOrError.SetType(CBotErrNbParam); if ( TypeOrError.Eq(CBotErrUndefCall)) TypeOrError.SetType(CBotErrOverParam); continue; // to many parameters } if ( pv != nullptr ) { - if ( pFunc != nullptr ) continue; + if ( !funcMap.empty() ) continue; // previous useable function if ( TypeOrError.Eq(CBotErrOverParam) ) TypeOrError.SetType(CBotErrNbParam); if ( TypeOrError.Eq(CBotErrUndefCall) ) TypeOrError.SetType(CBotErrLowParam); continue; // not enough parameters } - - if (alpha == 0) // perfect signature - { - nIdent = pt->m_nFuncIdent; - TypeOrError = pt->m_retTyp; - return pt; - } - - if ( alpha < delta ) // a better signature? - { - pFunc = pt; - delta = alpha; - } + funcMap.insert( std::pair(pt, alpha) ); } } } - if ( pFunc != nullptr ) + if ( !funcMap.empty() ) { + auto it = funcMap.begin(); + CBotFunction* pFunc = it->first; // the best function found + signed int delta = it->second; // seeks the lowest signature + + for (++it ; it != funcMap.end() ; it++) + { + if (it->second < delta) // a better signature? + { + TypeOrError.SetType(CBotNoErr); + pFunc = it->first; + delta = it->second; + continue; + } + + if (it->second == delta) TypeOrError.SetType(CBotErrAmbiguousCall); + } + + if (TypeOrError.Eq(CBotErrAmbiguousCall)) return nullptr; nIdent = pFunc->m_nFuncIdent; TypeOrError = pFunc->m_retTyp; return pFunc; diff --git a/src/CBot/CBotInstr/CBotInstrUtils.cpp b/src/CBot/CBotInstr/CBotInstrUtils.cpp index bbe80b27..067f443a 100644 --- a/src/CBot/CBotInstr/CBotInstrUtils.cpp +++ b/src/CBot/CBotInstr/CBotInstrUtils.cpp @@ -149,12 +149,20 @@ bool TypesCompatibles(const CBotTypResult& type1, const CBotTypResult& type2) if (max >= CBotTypBoolean) { + if (t1 == CBotTypPointer && t2 == CBotTypNullPointer) return true; if (t2 != t1) return false; + if (max == CBotTypPointer) + { + CBotClass* c1 = type1.GetClass(); + CBotClass* c2 = type2.GetClass(); + return c2->IsChildOf(c1); + } + if (max == CBotTypArrayPointer) return TypesCompatibles(type1.GetTypElem(), type2.GetTypElem()); - if (max == CBotTypClass || max == CBotTypPointer) + if (max == CBotTypClass) return type1.GetClass() == type2.GetClass() ; return true ; diff --git a/src/common/restext.cpp b/src/common/restext.cpp index 9770c32a..859a61f9 100644 --- a/src/common/restext.cpp +++ b/src/common/restext.cpp @@ -717,6 +717,7 @@ void InitializeRestext() stringsCbot[CBot::CBotErrPrivate] = TR("Private element"); stringsCbot[CBot::CBotErrNoPublic] = TR("Public required"); stringsCbot[CBot::CBotErrNoExpression] = TR("Expression expected after ="); + stringsCbot[CBot::CBotErrAmbiguousCall] = TR("Ambiguous call to overloaded function"); stringsCbot[CBot::CBotErrZeroDiv] = TR("Dividing by zero"); stringsCbot[CBot::CBotErrNotInit] = TR("Variable not initialized"); diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index 071abb94..2a112d77 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -1786,3 +1786,91 @@ TEST_F(CBotUT, ClassNewConstructorMethodChain) "}\n" ); } + +TEST_F(CBotUT, PassNullAsArgument) +{ + auto publicProgram = ExecuteTest( + "public class BaseClass {}\n" + "public class SubClass extends BaseClass {}\n" + ); + + ExecuteTest( + "bool Test(BaseClass b) {\n" + " return (b == null);\n" + "}\n" + "extern void PassNullAsArgument() {\n" + " ASSERT(true == Test(null));\n" + "}\n" + ); + + ExecuteTest( + "void Test(BaseClass b) {}\n" + "void Test(SubClass s) {}\n" + "\n" + "extern void AmbiguousCallArgumentNull() {\n" + " Test(null);\n" + "}\n", + CBotErrAmbiguousCall + ); +} + +TEST_F(CBotUT, ClassImplicitCastArguments) +{ + auto publicProgram = ExecuteTest( + "public class BaseClass { int a = 360; }\n" + "public class SubClass extends BaseClass {}\n" + ); + + ExecuteTest( + "bool Test(BaseClass b) {\n" + " SubClass s = b;\n" + " return (360 == s.a);\n" + "}\n" + "extern void UpcastPassingArguments() {\n" + " ASSERT(true == Test(new SubClass()));\n" + "}\n" + ); + + ExecuteTest( + "void Test(BaseClass b, SubClass s) {}\n" + "void Test(SubClass s, BaseClass b) {}\n" + "\n" + "extern void UpcastAmbiguousCall() {\n" + " Test(new SubClass(), new SubClass());\n" + "}\n", + CBotErrAmbiguousCall + ); + + ExecuteTest( + "bool Test(BaseClass b, SubClass s) { return false; }\n" + "bool Test(SubClass s, BaseClass b) { return false; }\n" + "bool Test(SubClass s, SubClass s2) { return true; }\n" + "\n" + "extern void NoErrorMoreSpecific() {\n" + " ASSERT(true == Test(new SubClass(), new SubClass()));\n" + "}\n" + ); +} + +TEST_F(CBotUT, AmbiguousCallWithNumbers) +{ + ExecuteTest( + "void Test(int i, float f) {}\n" + "void Test(float f, int i) {}\n" + "\n" + "extern void AmbiguousCallNumbers() {\n" + " Test(1, 2);\n" + "}\n", + CBotErrAmbiguousCall + ); + + ExecuteTest( + "bool Test(int i, float f) { return false; }\n" + "bool Test(float f, int i) { return false; }\n" + "bool Test(int i, int ii) { return true; }\n" + "\n" + "extern void NoErrorMoreSpecific() {\n" + " ASSERT(true == Test(1, 2));\n" + "}\n" + ); +} From 3debfb918288ebc17621cb15139eabe0d849f28d Mon Sep 17 00:00:00 2001 From: melex750 Date: Sat, 17 Sep 2016 07:59:34 -0400 Subject: [PATCH 41/56] Fix finding in-class methods when searching by ID --- src/CBot/CBotClass.cpp | 10 +--------- src/CBot/CBotInstr/CBotFunction.cpp | 4 ---- src/CBot/CBotProgram.cpp | 1 + test/unit/CBot/CBot_test.cpp | 30 +++++++++++++++++++++++++++++ 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/CBot/CBotClass.cpp b/src/CBot/CBotClass.cpp index a0ec6402..b342ab42 100644 --- a/src/CBot/CBotClass.cpp +++ b/src/CBot/CBotClass.cpp @@ -639,7 +639,6 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond) { // return a method precompiled in pass 1 CBotFunction* pf = m_pMethod; - CBotFunction* prev = nullptr; CBotToken* ppp = p; CBotCStack* pStk = pStack->TokenStack(nullptr, true); CBotDefParam* params = CBotDefParam::Compile(p, pStk ); @@ -648,7 +647,6 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond) while ( pf != nullptr ) // search by name and parameters { if (pf->GetName() == pp && pf->CheckParam( params )) break; - prev = pf; pf = pf->Next(); } @@ -693,18 +691,12 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond) // compiles a method p = pBase; CBotFunction* f = - CBotFunction::Compile(p, pile, nullptr/*, false*/); + CBotFunction::Compile(p, pile, pf/*, false*/); if ( f != nullptr ) { f->m_pProg = pStack->GetProgram(); f->m_bSynchro = bSynchro; - // replaces the element in the chain - f->m_next = pf->m_next; - pf->m_next = nullptr; - delete pf; - if (prev == nullptr) m_pMethod = f; - else prev->m_next = f; } pStack->Return(nullptr, pile); } diff --git a/src/CBot/CBotInstr/CBotFunction.cpp b/src/CBot/CBotInstr/CBotFunction.cpp index 1e7e0b94..0b1e05b5 100644 --- a/src/CBot/CBotInstr/CBotFunction.cpp +++ b/src/CBot/CBotInstr/CBotFunction.cpp @@ -224,10 +224,6 @@ CBotFunction* CBotFunction::Compile(CBotToken* &p, CBotCStack* pStack, CBotFunct func->m_closeblk = (p != nullptr && p->GetPrev() != nullptr) ? *(p->GetPrev()) : CBotToken(); if ( pStk->IsOk() ) { - if ( func->m_bPublic ) // public function, return known for all - { - CBotFunction::AddPublic(func); - } return pStack->ReturnFunc(func, pStk); } } diff --git a/src/CBot/CBotProgram.cpp b/src/CBot/CBotProgram.cpp index 77dac399..c406a01a 100644 --- a/src/CBot/CBotProgram.cpp +++ b/src/CBot/CBotProgram.cpp @@ -132,6 +132,7 @@ bool CBotProgram::Compile(const std::string& program, std::vector& m_bCompileClass = false; CBotFunction::Compile(p, pStack.get(), next); if (next->IsExtern()) functions.push_back(next->GetName()/* + next->GetParams()*/); + if (next->IsPublic()) CBotFunction::AddPublic(next); next->m_pProg = this; // keeps pointers to the module next = next->Next(); } diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index 2a112d77..fb63ed91 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -1874,3 +1874,33 @@ TEST_F(CBotUT, AmbiguousCallWithNumbers) "}\n" ); } + +TEST_F(CBotUT, ClassMethodWithPublicKeyword) +{ + auto publicProgram = ExecuteTest( + "public class TestClass {\n" + " public int Test() { return 1; }\n" + "}\n" + ); + + ExecuteTest( + "int Test() { return 2; }\n" + "\n" + "extern void DontCallMethodInTestClass()\n" + "{\n" + " ASSERT(2 == Test());\n" + "}\n" + ); + + ExecuteTest( + "int Test() { return 2; }\n" + "\n" + "public class OtherClass {}\n" + "\n" + "extern void OtherClass::TestCallWithThis()\n" + "{\n" + " this.Test();\n" + "}\n", + CBotErrUndefCall + ); +} From 9ab7f7d140d779ef23323d95f3a01569d997a889 Mon Sep 17 00:00:00 2001 From: melex750 Date: Sat, 17 Sep 2016 08:00:34 -0400 Subject: [PATCH 42/56] Fix access to protected and private variables --- src/CBot/CBotClass.cpp | 1 + src/CBot/CBotInstr/CBotExprRetVar.cpp | 4 +- src/CBot/CBotInstr/CBotExprVar.cpp | 7 +- src/CBot/CBotInstr/CBotFieldExpr.cpp | 53 +++++++++ src/CBot/CBotInstr/CBotFieldExpr.h | 11 ++ src/CBot/CBotInstr/CBotLeftExpr.cpp | 7 +- src/CBot/CBotProgram.cpp | 2 - src/CBot/CBotProgram.h | 7 -- test/unit/CBot/CBot_test.cpp | 149 ++++++++++++++++++++++++++ 9 files changed, 222 insertions(+), 19 deletions(-) diff --git a/src/CBot/CBotClass.cpp b/src/CBot/CBotClass.cpp index b342ab42..460ab9fa 100644 --- a/src/CBot/CBotClass.cpp +++ b/src/CBot/CBotClass.cpp @@ -682,6 +682,7 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond) initType = CBotVar::InitType::DEF; pcopy->SetInit(initType); pcopy->SetUniqNum(pv->GetUniqNum()); + pcopy->SetPrivate(pv->GetPrivate()); pile->AddVar(pcopy); pv = pv->GetNext(); } diff --git a/src/CBot/CBotInstr/CBotExprRetVar.cpp b/src/CBot/CBotInstr/CBotExprRetVar.cpp index 96ff03c0..c0e0e18f 100644 --- a/src/CBot/CBotInstr/CBotExprRetVar.cpp +++ b/src/CBot/CBotInstr/CBotExprRetVar.cpp @@ -105,12 +105,12 @@ CBotInstr* CBotExprRetVar::Compile(CBotToken*& p, CBotCStack* pStack, bool bMeth CBotFieldExpr* i = new CBotFieldExpr(); i->SetToken(pp); inst->AddNext3(i); + CBotVar* preVar = var; var = var->GetItem(p->GetString()); if (var != nullptr) { i->SetUniqNum(var->GetUniqNum()); - if ( var->IsPrivate() && - !pStk->GetProgram()->m_bCompileClass) + if (CBotFieldExpr::ProtectionError(pStk, preVar, var)) { pStk->SetError(CBotErrPrivate, pp); goto err; diff --git a/src/CBot/CBotInstr/CBotExprVar.cpp b/src/CBot/CBotInstr/CBotExprVar.cpp index 3b774caf..1963d30a 100644 --- a/src/CBot/CBotInstr/CBotExprVar.cpp +++ b/src/CBot/CBotInstr/CBotExprVar.cpp @@ -67,8 +67,7 @@ CBotInstr* CBotExprVar::Compile(CBotToken*& p, CBotCStack* pStack, CBotVar::Prot if (ident > 0 && ident < 9000) { - if ( var->IsPrivate(privat) && - !pStk->GetProgram()->m_bCompileClass) + if (CBotFieldExpr::ProtectionError(pStk, nullptr, var, privat)) { pStk->SetError(CBotErrPrivate, p); goto err; @@ -133,12 +132,12 @@ CBotInstr* CBotExprVar::Compile(CBotToken*& p, CBotCStack* pStack, CBotVar::Prot CBotFieldExpr* i = new CBotFieldExpr(); // new element i->SetToken(pp); // keeps the name of the token inst->AddNext3(i); // add after + CBotVar* preVar = var; var = var->GetItem(p->GetString()); // get item correspondent if (var != nullptr) { i->SetUniqNum(var->GetUniqNum()); - if ( var->IsPrivate() && - !pStk->GetProgram()->m_bCompileClass) + if (CBotFieldExpr::ProtectionError(pStk, preVar, var, privat)) { pStk->SetError(CBotErrPrivate, pp); goto err; diff --git a/src/CBot/CBotInstr/CBotFieldExpr.cpp b/src/CBot/CBotInstr/CBotFieldExpr.cpp index 56ba987c..1d12a657 100644 --- a/src/CBot/CBotInstr/CBotFieldExpr.cpp +++ b/src/CBot/CBotInstr/CBotFieldExpr.cpp @@ -134,4 +134,57 @@ std::string CBotFieldExpr::GetDebugData() return ss.str(); } +//////////////////////////////////////////////////////////////////////////////// +bool CBotFieldExpr::ProtectionError(CBotCStack* pStack, CBotVar* pPrev, CBotVar* pVar, + CBotVar::ProtectionLevel privat) +{ + CBotVar::ProtectionLevel varPriv = pVar->GetPrivate(); + + if (privat == CBotVar::ProtectionLevel::ReadOnly && varPriv == privat) + return true; + + if (varPriv == CBotVar::ProtectionLevel::Public) return false; + + std::string prevName = (pPrev == nullptr) ? "" : pPrev->GetName(); + + // implicit 'this.'var, this.var, or super.var + if (pPrev == nullptr || prevName == "this" || prevName == "super") // member of the current class + { + if (varPriv == CBotVar::ProtectionLevel::Private) // var is private ? + { + CBotToken token("this"); + CBotVar* pThis = pStack->FindVar(token); + CBotClass* pClass = pThis->GetClass(); // the current class + + CBotVar* pVarList = pClass->GetVar(); + + int ident = pVar->GetUniqNum(); + // check if var is inherited from a parent class + if (pVarList == nullptr || ident < pVarList->GetUniqNum()) + return true; + } + } + else // any other context + { + if (pVar->IsPrivate()) // var is protected or private ? + { + CBotToken token("this"); + CBotVar* pThis = pStack->FindVar(token); + + if (pThis == nullptr) return true; // inside a function ? + if (pThis->GetType() != CBotTypPointer) return true; + + CBotClass* pClass = pThis->GetClass(); // the current class + + if (!pClass->IsChildOf(pPrev->GetClass())) // var is member of some other class ? + return true; + + if (varPriv == CBotVar::ProtectionLevel::Private && // private member of a parent class + pClass != pPrev->GetClass()) return true; + } + } + + return false; +} + } // namespace CBot diff --git a/src/CBot/CBotInstr/CBotFieldExpr.h b/src/CBot/CBotInstr/CBotFieldExpr.h index db48839b..2ee2851a 100644 --- a/src/CBot/CBotInstr/CBotFieldExpr.h +++ b/src/CBot/CBotInstr/CBotFieldExpr.h @@ -65,6 +65,17 @@ public: */ void RestoreStateVar(CBotStack* &pj, bool bMain) override; + /*! + * \brief ProtectionError Test if access to a variable is not allowed. + * \param pStack + * \param pPrev + * \param pVar + * \param privat + * \return True if pVar is protected in the current context. + */ + static bool ProtectionError(CBotCStack* pStack, CBotVar* pPrev, CBotVar* pVar, + CBotVar::ProtectionLevel privat = CBotVar::ProtectionLevel::Protected); + protected: virtual const std::string GetDebugName() override { return "CBotFieldExpr"; } virtual std::string GetDebugData() override; diff --git a/src/CBot/CBotInstr/CBotLeftExpr.cpp b/src/CBot/CBotInstr/CBotLeftExpr.cpp index 3a9fe155..8201137e 100644 --- a/src/CBot/CBotInstr/CBotLeftExpr.cpp +++ b/src/CBot/CBotInstr/CBotLeftExpr.cpp @@ -64,8 +64,7 @@ CBotLeftExpr* CBotLeftExpr::Compile(CBotToken* &p, CBotCStack* pStack) inst->m_nIdent = var->GetUniqNum(); if (inst->m_nIdent > 0 && inst->m_nIdent < 9000) { - if ( var->IsPrivate(CBotVar::ProtectionLevel::ReadOnly) && - !pStk->GetProgram()->m_bCompileClass) + if (CBotFieldExpr::ProtectionError(pStk, nullptr, var, CBotVar::ProtectionLevel::ReadOnly)) { pStk->SetError(CBotErrPrivate, p); goto err; @@ -125,11 +124,11 @@ CBotLeftExpr* CBotLeftExpr::Compile(CBotToken* &p, CBotCStack* pStack) if (p->GetType() == TokenTypVar) // must be a name { + CBotVar* preVar = var; var = var->GetItem(p->GetString()); // get item correspondent if (var != nullptr) { - if ( var->IsPrivate(CBotVar::ProtectionLevel::ReadOnly) && - !pStk->GetProgram()->m_bCompileClass) + if (CBotFieldExpr::ProtectionError(pStk, preVar, var, CBotVar::ProtectionLevel::ReadOnly)) { pStk->SetError(CBotErrPrivate, pp); goto err; diff --git a/src/CBot/CBotProgram.cpp b/src/CBot/CBotProgram.cpp index c406a01a..81008297 100644 --- a/src/CBot/CBotProgram.cpp +++ b/src/CBot/CBotProgram.cpp @@ -124,12 +124,10 @@ bool CBotProgram::Compile(const std::string& program, std::vector& if ( p->GetType() == ID_CLASS || ( p->GetType() == ID_PUBLIC && p->GetNext()->GetType() == ID_CLASS )) { - m_bCompileClass = true; CBotClass::Compile(p, pStack.get()); // completes the definition of the class } else { - m_bCompileClass = false; CBotFunction::Compile(p, pStack.get(), next); if (next->IsExtern()) functions.push_back(next->GetName()/* + next->GetParams()*/); if (next->IsPublic()) CBotFunction::AddPublic(next); diff --git a/src/CBot/CBotProgram.h b/src/CBot/CBotProgram.h index 9f5cdc00..165defc1 100644 --- a/src/CBot/CBotProgram.h +++ b/src/CBot/CBotProgram.h @@ -332,13 +332,6 @@ public: */ CBotFunction* GetFunctions(); - /** - * \brief true while compiling class - * - * TODO: refactor this - */ - bool m_bCompileClass; - /** * \brief Returns static list of all registered external calls */ diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index fb63ed91..797a674e 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -1904,3 +1904,152 @@ TEST_F(CBotUT, ClassMethodWithPublicKeyword) CBotErrUndefCall ); } + +TEST_F(CBotUT, ClassTestProtectedMember) +{ + auto publicProgram = ExecuteTest( + "public class BaseClass {\n" + " protected int a_protected = 1;\n" + " bool test() {\n" + " a_protected = 1;\n" + " int a = a_protected;\n" + " return true;\n" + " }\n" + "}\n" + "extern void Test() {\n" + " BaseClass b();\n" + " ASSERT(true == b.test());\n" + "}\n" + ); + + ExecuteTest( + "public class SubClass extends BaseClass {\n" + " bool testProtected() {\n" + " a_protected = 1;\n" + " int a = a_protected;\n" + " return true;\n" + " }\n" + "}\n" + "extern void TestSubClassAccessProtected() {\n" + " SubClass s();\n" + " ASSERT(true == s.test());\n" + " ASSERT(true == s.testProtected());\n" + "}\n" + ); + + ExecuteTest( + "extern void TestErrorProtected() {\n" + " BaseClass b();\n" + " int i = b.a_protected;\n" + "}\n", + CBotErrPrivate + ); + + ExecuteTest( + "extern void ErrorProtectedAssignment() {\n" + " BaseClass b();\n" + " b.a_protected = 1;\n" + "}\n", + CBotErrPrivate + ); + + ExecuteTest( + "public class SomeOtherClass {\n" + " void testErrorProtected() {\n" + " BaseClass b();\n" + " int i = b.a_protected;\n" + " }\n" + "}\n", + CBotErrPrivate + ); + + ExecuteTest( + "public class SomeOtherClass {\n" + " void testErrorProtectedAssignment() {\n" + " BaseClass b();\n" + " b.a_protected = 1;\n" + " }\n" + "}\n", + CBotErrPrivate + ); +} + +TEST_F(CBotUT, ClassTestPrivateMember) +{ + auto publicProgram = ExecuteTest( + "public class BaseClass {\n" + " private int a_private = 2;\n" + "\n" + " bool test() {\n" + " a_private = 2;\n" + " int a = a_private;\n" + " return true;\n" + " }\n" + " bool NoErrorPrivateSameClass() {\n" + " BaseClass b = new BaseClass();\n" + " int a = b.a_private;\n" + " b.a_private = 2;\n" + " return true;\n" + " }\n" + "}\n" + "extern void Test() {\n" + " BaseClass b();\n" + " ASSERT(true == b.test());\n" + " ASSERT(true == b.NoErrorPrivateSameClass());\n" + "}\n" + ); + + ExecuteTest( + "public class SubClass extends BaseClass {\n" + " void testErrorPrivate() {\n" + " int a = a_private;\n" + " }\n" + "}\n", + CBotErrPrivate + ); + + ExecuteTest( + "public class SubClass extends BaseClass {\n" + " void testErrorPrivateAssignment() {\n" + " a_private = 2;\n" + " }\n" + "}\n", + CBotErrPrivate + ); + + ExecuteTest( + "extern void TestErrorPrivate() {\n" + " BaseClass b();\n" + " int i = b.a_private;\n" + "}\n", + CBotErrPrivate + ); + + ExecuteTest( + "extern void ErrorPrivateAssignment() {\n" + " BaseClass b();\n" + " b.a_private = 2;\n" + "}\n", + CBotErrPrivate + ); + + ExecuteTest( + "public class SomeOtherClass {\n" + " void testErrorPrivate() {\n" + " BaseClass b();\n" + " int i = b.a_private;\n" + " }\n" + "}\n", + CBotErrPrivate + ); + + ExecuteTest( + "public class SomeOtherClass {\n" + " void testErrorPrivateAssignment() {\n" + " BaseClass b();\n" + " b.a_private = 1;\n" + " }\n" + "}\n", + CBotErrPrivate + ); +} From 81aae35565b969a1c5ba5c50b4af99405fdbda5c Mon Sep 17 00:00:00 2001 From: krzys-h Date: Sat, 24 Sep 2016 16:48:27 +0200 Subject: [PATCH 43/56] Updated translation files after 4a14a44f3f07ee103161fbec2bcaa4bf5e6e2a84 --- po/colobot.pot | 3 +++ po/de.po | 3 +++ po/fr.po | 3 +++ po/pl.po | 3 +++ po/ru.po | 3 +++ 5 files changed, 15 insertions(+) diff --git a/po/colobot.pot b/po/colobot.pot index 91bfcdbd..1f183eea 100644 --- a/po/colobot.pot +++ b/po/colobot.pot @@ -1730,6 +1730,9 @@ msgstr "" msgid "Expression expected after =" msgstr "" +msgid "Ambiguous call to overloaded function" +msgstr "" + msgid "Dividing by zero" msgstr "" diff --git a/po/de.po b/po/de.po index be31b511..05a31006 100644 --- a/po/de.po +++ b/po/de.po @@ -86,6 +86,9 @@ msgstr "Trägt schon etwas" msgid "Alternative camera mode\\Move sideways instead of rotating (in free camera)" msgstr "" +msgid "Ambiguous call to overloaded function" +msgstr "" + msgid "Analysis already performed" msgstr "Analyse schon durchgeführt" diff --git a/po/fr.po b/po/fr.po index 996fc6fb..9fb7a344 100644 --- a/po/fr.po +++ b/po/fr.po @@ -81,6 +81,9 @@ msgstr "Porte déjà quelque chose" msgid "Alternative camera mode\\Move sideways instead of rotating (in free camera)" msgstr "" +msgid "Ambiguous call to overloaded function" +msgstr "" + msgid "Analysis already performed" msgstr "Analyse déjà effectuée" diff --git a/po/pl.po b/po/pl.po index 1b0dc6d4..d9cde920 100644 --- a/po/pl.po +++ b/po/pl.po @@ -84,6 +84,9 @@ msgstr "Nie można nieść więcej przedmiotów" msgid "Alternative camera mode\\Move sideways instead of rotating (in free camera)" msgstr "Alternatywny tryb kamery\\Poruszaj na boki zamiast obracać (w kamerze swobodnej)" +msgid "Ambiguous call to overloaded function" +msgstr "Niejednoznaczne wywołanie przeciążonej funkcji" + msgid "Analysis already performed" msgstr "Analiza została już wykonana" diff --git a/po/ru.po b/po/ru.po index 2e375df6..a6c1a61f 100644 --- a/po/ru.po +++ b/po/ru.po @@ -84,6 +84,9 @@ msgstr "Уже что-то несу" msgid "Alternative camera mode\\Move sideways instead of rotating (in free camera)" msgstr "Альтернативный режим камеры\\Движение в стороны вместо поворачивания (в режиме свободной камеры)" +msgid "Ambiguous call to overloaded function" +msgstr "" + msgid "Analysis already performed" msgstr "Анализ уже выполнен" From ca548e29029df2c4c7eab818b9fc8713e3f8e22f Mon Sep 17 00:00:00 2001 From: krzys-h Date: Sat, 24 Sep 2016 17:04:47 +0200 Subject: [PATCH 44/56] Update CBotFieldExpr::ProtectionError docs --- src/CBot/CBotInstr/CBotExprRetVar.cpp | 2 +- src/CBot/CBotInstr/CBotExprVar.cpp | 4 ++-- src/CBot/CBotInstr/CBotFieldExpr.cpp | 4 ++-- src/CBot/CBotInstr/CBotFieldExpr.h | 20 ++++++++++++-------- src/CBot/CBotInstr/CBotLeftExpr.cpp | 5 +++-- src/CBot/CBotTypResult.h | 2 +- 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/CBot/CBotInstr/CBotExprRetVar.cpp b/src/CBot/CBotInstr/CBotExprRetVar.cpp index c0e0e18f..643a4690 100644 --- a/src/CBot/CBotInstr/CBotExprRetVar.cpp +++ b/src/CBot/CBotInstr/CBotExprRetVar.cpp @@ -110,7 +110,7 @@ CBotInstr* CBotExprRetVar::Compile(CBotToken*& p, CBotCStack* pStack, bool bMeth if (var != nullptr) { i->SetUniqNum(var->GetUniqNum()); - if (CBotFieldExpr::ProtectionError(pStk, preVar, var)) + if (CBotFieldExpr::CheckProtectionError(pStk, preVar, var)) { pStk->SetError(CBotErrPrivate, pp); goto err; diff --git a/src/CBot/CBotInstr/CBotExprVar.cpp b/src/CBot/CBotInstr/CBotExprVar.cpp index 1963d30a..a15c9c9a 100644 --- a/src/CBot/CBotInstr/CBotExprVar.cpp +++ b/src/CBot/CBotInstr/CBotExprVar.cpp @@ -67,7 +67,7 @@ CBotInstr* CBotExprVar::Compile(CBotToken*& p, CBotCStack* pStack, CBotVar::Prot if (ident > 0 && ident < 9000) { - if (CBotFieldExpr::ProtectionError(pStk, nullptr, var, privat)) + if (CBotFieldExpr::CheckProtectionError(pStk, nullptr, var, privat)) { pStk->SetError(CBotErrPrivate, p); goto err; @@ -137,7 +137,7 @@ CBotInstr* CBotExprVar::Compile(CBotToken*& p, CBotCStack* pStack, CBotVar::Prot if (var != nullptr) { i->SetUniqNum(var->GetUniqNum()); - if (CBotFieldExpr::ProtectionError(pStk, preVar, var, privat)) + if (CBotFieldExpr::CheckProtectionError(pStk, preVar, var, privat)) { pStk->SetError(CBotErrPrivate, pp); goto err; diff --git a/src/CBot/CBotInstr/CBotFieldExpr.cpp b/src/CBot/CBotInstr/CBotFieldExpr.cpp index 1d12a657..09ac5f8e 100644 --- a/src/CBot/CBotInstr/CBotFieldExpr.cpp +++ b/src/CBot/CBotInstr/CBotFieldExpr.cpp @@ -135,8 +135,8 @@ std::string CBotFieldExpr::GetDebugData() } //////////////////////////////////////////////////////////////////////////////// -bool CBotFieldExpr::ProtectionError(CBotCStack* pStack, CBotVar* pPrev, CBotVar* pVar, - CBotVar::ProtectionLevel privat) +bool CBotFieldExpr::CheckProtectionError(CBotCStack* pStack, CBotVar* pPrev, CBotVar* pVar, + CBotVar::ProtectionLevel privat) { CBotVar::ProtectionLevel varPriv = pVar->GetPrivate(); diff --git a/src/CBot/CBotInstr/CBotFieldExpr.h b/src/CBot/CBotInstr/CBotFieldExpr.h index 2ee2851a..67ab2f05 100644 --- a/src/CBot/CBotInstr/CBotFieldExpr.h +++ b/src/CBot/CBotInstr/CBotFieldExpr.h @@ -66,15 +66,19 @@ public: void RestoreStateVar(CBotStack* &pj, bool bMain) override; /*! - * \brief ProtectionError Test if access to a variable is not allowed. - * \param pStack - * \param pPrev - * \param pVar - * \param privat - * \return True if pVar is protected in the current context. + * \brief Check if access to a variable is allowed or not depending on public/private/protected setting + * + * If this function returns true, the caller is responsible for failing the compilation with ::CBotErrPrivate error. + * This function doesn't set the error flag itself. + * + * \param pStack Current compilation stack frame + * \param pPrev Class instance which variable to check is part of, or nullptr if not part of a class + * \param pVar Variable to check + * \param privat CBotVar::ProtectionLevel::ReadOnly if requesting read-only access, anything else otherwise + * \return true if pVar is inaccessible in the current context, false if access should be allowed */ - static bool ProtectionError(CBotCStack* pStack, CBotVar* pPrev, CBotVar* pVar, - CBotVar::ProtectionLevel privat = CBotVar::ProtectionLevel::Protected); + static bool CheckProtectionError(CBotCStack* pStack, CBotVar* pPrev, CBotVar* pVar, + CBotVar::ProtectionLevel privat = CBotVar::ProtectionLevel::Protected); protected: virtual const std::string GetDebugName() override { return "CBotFieldExpr"; } diff --git a/src/CBot/CBotInstr/CBotLeftExpr.cpp b/src/CBot/CBotInstr/CBotLeftExpr.cpp index 8201137e..4678ff8e 100644 --- a/src/CBot/CBotInstr/CBotLeftExpr.cpp +++ b/src/CBot/CBotInstr/CBotLeftExpr.cpp @@ -64,7 +64,7 @@ CBotLeftExpr* CBotLeftExpr::Compile(CBotToken* &p, CBotCStack* pStack) inst->m_nIdent = var->GetUniqNum(); if (inst->m_nIdent > 0 && inst->m_nIdent < 9000) { - if (CBotFieldExpr::ProtectionError(pStk, nullptr, var, CBotVar::ProtectionLevel::ReadOnly)) + if (CBotFieldExpr::CheckProtectionError(pStk, nullptr, var, CBotVar::ProtectionLevel::ReadOnly)) { pStk->SetError(CBotErrPrivate, p); goto err; @@ -128,7 +128,8 @@ CBotLeftExpr* CBotLeftExpr::Compile(CBotToken* &p, CBotCStack* pStack) var = var->GetItem(p->GetString()); // get item correspondent if (var != nullptr) { - if (CBotFieldExpr::ProtectionError(pStk, preVar, var, CBotVar::ProtectionLevel::ReadOnly)) + if (CBotFieldExpr::CheckProtectionError(pStk, preVar, var, + CBotVar::ProtectionLevel::ReadOnly)) { pStk->SetError(CBotErrPrivate, pp); goto err; diff --git a/src/CBot/CBotTypResult.h b/src/CBot/CBotTypResult.h index cd8d7468..00683ca5 100644 --- a/src/CBot/CBotTypResult.h +++ b/src/CBot/CBotTypResult.h @@ -106,7 +106,7 @@ public: /** * \brief Returns ::CBotType or ::CBotError stored in this object - * \param mode Mode, see ::GetTypeMode enum + * \param mode Mode, see GetTypeMode enum */ int GetType(GetTypeMode mode = GetTypeMode::NORMAL) const; From dc415c3d2a77f7f447d87118e49f1f53d9a4595a Mon Sep 17 00:00:00 2001 From: MatiRg Date: Sun, 25 Sep 2016 19:13:04 +0200 Subject: [PATCH 45/56] Remove character limit in CEdit (#836) --- src/level/robotmain.cpp | 4 +- .../implementation/program_storage_impl.cpp | 1 - src/script/script.cpp | 15 +- src/ui/controls/edit.cpp | 240 ++++++++---------- src/ui/controls/edit.h | 30 +-- src/ui/controls/editvalue.cpp | 6 +- src/ui/screen/screen_io.cpp | 9 +- src/ui/screen/screen_player_select.cpp | 22 +- src/ui/studio.cpp | 75 +++--- 9 files changed, 180 insertions(+), 222 deletions(-) diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index 23c9c9db..cfb98b4e 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -780,10 +780,10 @@ bool CRobotMain::ProcessEvent(Event &event) if (event.type == EVENT_KEY_DOWN && event.GetData()->key == KEY(RETURN) && m_cmdEdit) { - char cmd[50]; + std::string cmd; Ui::CEdit* pe = static_cast(m_interface->SearchControl(EVENT_CMD)); if (pe == nullptr) return false; - pe->GetText(cmd, 50); + cmd = pe->GetText(50); pe->SetText(""); pe->ClearState(Ui::STATE_VISIBLE); m_interface->SetFocus(nullptr); diff --git a/src/object/implementation/program_storage_impl.cpp b/src/object/implementation/program_storage_impl.cpp index c0fad70b..dc7e71d2 100644 --- a/src/object/implementation/program_storage_impl.cpp +++ b/src/object/implementation/program_storage_impl.cpp @@ -131,7 +131,6 @@ Program* CProgramStorageObjectImpl::CloneProgram(Program* program) // TODO: Is there any reason CScript doesn't have a function to get the program code directly? auto edit = MakeUnique(); - edit->SetMaxChar(Ui::EDITSTUDIOMAX); program->script->PutScript(edit.get(), ""); newprog->script->GetScript(edit.get()); diff --git a/src/script/script.cpp b/src/script/script.cpp index ff4c04d2..8253caea 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -105,9 +105,11 @@ void CScript::PutScript(Ui::CEdit* edit, const char* name) bool CScript::GetScript(Ui::CEdit* edit) { int len = edit->GetTextLength(); - m_script = MakeUniqueArray(len+1); + m_script = MakeUniqueArray(len+2); + + std::string tmp = edit->GetText(len+1); + strncpy(m_script.get(), tmp.c_str(), len+1); - edit->GetText(m_script.get(), len+1); edit->GetCursor(m_cursor2, m_cursor1); m_len = strlen(m_script.get()); @@ -592,13 +594,13 @@ void CScript::UpdateList(Ui::CList* list) void CScript::ColorizeScript(Ui::CEdit* edit, int rangeStart, int rangeEnd) { - if (rangeEnd > edit->GetMaxChar()) - rangeEnd = edit->GetMaxChar(); + if (rangeEnd > edit->GetTextLength()) + rangeEnd = edit->GetTextLength(); edit->SetFormat(rangeStart, rangeEnd, Gfx::FONT_HIGHLIGHT_COMMENT); // anything not processed is a comment // NOTE: Images are registered as index in some array, and that can be 0 which normally ends the string! - std::string text = std::string(edit->GetText(), edit->GetMaxChar()); + std::string text = edit->GetText(); text = text.substr(rangeStart, rangeEnd-rangeStart); auto tokens = CBot::CBotToken::CompileTokens(text.c_str()); @@ -927,7 +929,6 @@ bool CScript::SendScript(const char* text) if ( !Compile() ) return false;*/ Ui::CEdit* edit = m_interface->CreateEdit(Math::Point(0.0f, 0.0f), Math::Point(0.0f, 0.0f), 0, EVENT_EDIT9); - edit->SetMaxChar(Ui::EDITSTUDIOMAX); edit->SetAutoIndent(m_engine->GetEditIndentMode()); edit->SetText(text, true); GetScript(edit); @@ -947,7 +948,6 @@ bool CScript::ReadScript(const char* filename) m_script.reset(); edit = m_interface->CreateEdit(Math::Point(0.0f, 0.0f), Math::Point(0.0f, 0.0f), 0, EVENT_EDIT9); - edit->SetMaxChar(Ui::EDITSTUDIOMAX); edit->SetAutoIndent(m_engine->GetEditIndentMode()); edit->ReadText(filename); GetScript(edit); @@ -966,7 +966,6 @@ bool CScript::WriteScript(const char* filename) } Ui::CEdit* edit = m_interface->CreateEdit(Math::Point(0.0f, 0.0f), Math::Point(0.0f, 0.0f), 0, EVENT_EDIT9); - edit->SetMaxChar(Ui::EDITSTUDIOMAX); edit->SetAutoIndent(m_engine->GetEditIndentMode()); edit->SetText(m_script.get()); edit->WriteText(filename); diff --git a/src/ui/controls/edit.cpp b/src/ui/controls/edit.cpp index c50f9796..f453c8fd 100644 --- a/src/ui/controls/edit.cpp +++ b/src/ui/controls/edit.cpp @@ -60,7 +60,6 @@ const float BIG_FONT = 1.6f; - //! Indicates whether a character is a space. bool IsSpace(int character) @@ -90,11 +89,11 @@ bool IsSep(int character) //! Object's constructor. CEdit::CEdit() : CControl(), + m_maxChar( std::numeric_limits::max() ), + m_text(), m_lineOffset(), m_lineIndent() { - m_maxChar = 100; - m_text = std::vector(m_maxChar+1, '\0'); m_len = 0; m_fontType = Gfx::FONT_COURIER; @@ -558,7 +557,7 @@ bool CEdit::IsLinkPos(Math::Point pos) { int i; - if ( m_format.size() == 0 ) return false; + if ( m_format.empty() ) return false; i = MouseDetect(pos); if ( i == -1 ) return false; @@ -721,7 +720,7 @@ int CEdit::MouseDetect(Math::Point mouse) { len = m_lineOffset[i+1] - m_lineOffset[i]; - if ( m_format.size() == 0 ) + if ( m_format.empty() ) { // c = m_engine->GetText()->Detect(m_text.data()+m_lineOffset[i], // len, offset, m_fontSize, @@ -1018,7 +1017,7 @@ void CEdit::Draw() o1 = c1; if ( o1 < beg ) o1 = beg; o2 = c2; if ( o2 > beg+len ) o2 = beg+len; - if ( m_format.size() == 0 ) + if ( m_format.empty() ) { start.x = ppos.x+m_engine->GetText()->GetStringWidth(std::string(m_text.data()+beg).substr(0, o1-beg), m_fontType, size); end.x = m_engine->GetText()->GetStringWidth(std::string(m_text.data()+o1).substr(0, o2-o1), m_fontType, size); @@ -1052,7 +1051,7 @@ void CEdit::Draw() eol = 2; // square (eot) } if ( !m_bMulti || !m_bDisplaySpec ) eol = 0; - if ( m_format.size() == 0 ) + if ( m_format.empty() ) { m_engine->GetText()->DrawText(std::string(m_text.data()+beg).substr(0, len), m_fontType, size, ppos, m_dim.x, Gfx::TEXT_ALIGN_LEFT, eol); } @@ -1093,7 +1092,7 @@ void CEdit::Draw() len = m_cursor1 - m_lineOffset[i]; - if ( m_format.size() == 0 ) + if ( m_format.empty() ) { m_engine->GetText()->SizeText(std::string(m_text.data()+m_lineOffset[i]).substr(0, len), m_fontType, size, pos, Gfx::TEXT_ALIGN_LEFT, @@ -1255,106 +1254,82 @@ void CEdit::DrawColor(Math::Point pos, Math::Point dim, Gfx::Color color) // Give the text to edit. -void CEdit::SetText(const char *text, bool bNew) +void CEdit::SetText(const std::string& text, bool bNew) { int i, j, font; bool bBOL; if ( !bNew ) UndoMemorize(OPERUNDO_SPEC); - m_len = strlen(text); - if ( m_len > m_maxChar ) m_len = m_maxChar; + m_len = text.size(); - if ( m_format.size() == 0 ) + if( m_len >= GetMaxChar() ) m_len = GetMaxChar(); + + m_text.resize( m_len + 1, '\0' ); + m_format.resize( m_len + 1, m_fontType ); + + font = m_fontType; + j = 0; + bBOL = true; + for ( i=0 ; i max ) max = max-1; - strncpy(buffer, m_text.data(), max); - buffer[max] = 0; + return std::string( m_text, 0, max ); } // Returns the length of the text. @@ -1437,15 +1410,15 @@ void CEdit::FreeImage() // Read from a text file. -bool CEdit::ReadText(std::string filename, int addSize) +bool CEdit::ReadText(std::string filename) { - int len, i, j, n, font, iLines, iCount; + int len, len2, i, j, n, font, iLines, iCount; char iName[50]; float iWidth; InputSlot slot; bool bInSoluce, bBOL; - if ( filename == "" ) return false; + if ( filename.empty() ) return false; CInputStream stream; stream.open(filename); @@ -1457,26 +1430,22 @@ bool CEdit::ReadText(std::string filename, int addSize) } len = stream.size(); + len2 = len + 1; - m_maxChar = len+addSize+100; m_len = len; m_cursor1 = 0; m_cursor2 = 0; FreeImage(); - m_text = std::vector(m_maxChar+1, '\0'); + m_text = std::string(len2+1, '\0'); - std::vector buffer(m_maxChar+1, '\0'); + std::vector buffer(len2+1, '\0'); stream.read(buffer.data(), len); m_format.clear(); - m_format.reserve(m_maxChar+1); - for (i = 0; i <= m_maxChar+1; i++) - { - m_format.push_back(m_fontType); - } + m_format.resize(len2+1, m_fontType); stream.close(); @@ -1922,14 +1891,10 @@ void CEdit::SetMaxChar(int max) m_maxChar = max; - m_text = std::vector(m_maxChar+1, '\0'); + m_text.resize( m_maxChar + 1, '\0' ); m_format.clear(); - m_format.reserve(m_maxChar+1); - for (int i = 0; i <= m_maxChar+1; i++) - { - m_format.push_back(m_fontType); - } + m_format.resize(m_maxChar + 1, m_fontType); m_len = 0; m_cursor1 = 0; @@ -2121,11 +2086,7 @@ void CEdit::SetMultiFont(bool bMulti) if (bMulti) { - m_format.reserve(m_maxChar+1); - for (int i = 0; i <= m_maxChar+1; i++) - { - m_format.push_back(m_fontType); - } + m_format.resize( m_text.size() + 1, m_fontType ); } } @@ -2419,7 +2380,7 @@ void CEdit::MoveLine(int move, bool bWord, bool bSelect) column -= indentLength*m_lineIndent[line]; } - if ( m_format.size() == 0 ) + if ( m_format.empty() ) { c = m_engine->GetText()->Detect(std::string(m_text.data()+m_lineOffset[line]), m_fontType, m_fontSize, @@ -2450,7 +2411,7 @@ void CEdit::ColumnFix() line = GetCursorLine(m_cursor1); - if ( m_format.size() == 0 ) + if ( m_format.empty() ) { m_column = m_engine->GetText()->GetStringWidth( std::string(m_text.data()+m_lineOffset[line]), @@ -2706,7 +2667,10 @@ void CEdit::InsertOne(char character) DeleteOne(0); // deletes the selected characters } - if ( m_len >= m_maxChar ) return; + if ( m_len >= GetMaxChar() ) return; + + m_text.resize( m_text.size() + 1, '\0' ); + m_format.resize( m_format.size() + 1, m_fontType ); for ( i=m_len ; i>m_cursor1 ; i-- ) { @@ -2938,13 +2902,16 @@ bool CEdit::MinMaj(bool bMaj) void CEdit::Justif() { float width, size, indentLength = 0.0f; - int i, j, line, indent; + int i, j, k, line, indent; bool bDual, bString, bRem; + m_lineOffset.clear(); + m_lineIndent.clear(); + indent = 0; m_lineTotal = 0; - m_lineOffset[m_lineTotal] = 0; - m_lineIndent[m_lineTotal] = indent; + m_lineOffset.push_back( 0 ); + m_lineIndent.push_back( indent ); m_lineTotal ++; if ( m_bAutoIndent ) @@ -2954,7 +2921,7 @@ void CEdit::Justif() } bString = bRem = false; - i = 0; + i = k = 0; while ( true ) { bDual = false; @@ -2965,7 +2932,7 @@ void CEdit::Justif() width -= indentLength*m_lineIndent[m_lineTotal-1]; } - if ( m_format.size() == 0 ) + if ( m_format.empty() ) { // TODO check if good @@ -3014,26 +2981,27 @@ void CEdit::Justif() if ( indent < 0 ) indent = 0; } - m_lineOffset[m_lineTotal] = i; - m_lineIndent[m_lineTotal] = indent; + m_lineOffset.push_back( i ); + m_lineIndent.push_back( indent ); m_lineTotal ++; if ( bDual ) { - m_lineOffset[m_lineTotal] = i; - m_lineIndent[m_lineTotal] = indent; + m_lineOffset.push_back( i ); + m_lineIndent.push_back( indent ); m_lineTotal ++; } - if ( m_lineTotal >= EDITLINEMAX-2 ) break; + if ( k == i ) break; + k = i; } if ( m_len > 0 && m_text[m_len-1] == '\n' ) { - m_lineOffset[m_lineTotal] = m_len; - m_lineIndent[m_lineTotal] = 0; + m_lineOffset.push_back( m_len ); + m_lineIndent.push_back( 0 ); m_lineTotal ++; } - m_lineOffset[m_lineTotal] = m_len; - m_lineIndent[m_lineTotal] = 0; + m_lineOffset.push_back( m_len ); + m_lineIndent.push_back( 0 ); if ( m_bAutoIndent ) { @@ -3168,7 +3136,7 @@ bool CEdit::UndoRecall() bool CEdit::ClearFormat() { - if ( m_format.size() == 0 ) + if ( m_format.empty() ) { SetMultiFont(true); } diff --git a/src/ui/controls/edit.h b/src/ui/controls/edit.h index bb7b5e3c..4192a133 100644 --- a/src/ui/controls/edit.h +++ b/src/ui/controls/edit.h @@ -34,21 +34,15 @@ namespace Ui class CScroll; - -//! maximum number of characters in CBOT edit -const int EDITSTUDIOMAX = 20000; -//! maximum total number of lines -const int EDITLINEMAX = 1000; //! max number of levels preserves const int EDITHISTORYMAX = 50; - //! max number of successive undo const int EDITUNDOMAX = 20; struct EditUndo { //! original text - std::vector text; + std::string text; //! length of the text int len = 0; //! offset cursor @@ -124,12 +118,12 @@ public: bool EventProcess(const Event &event) override; void Draw() override; - void SetText(const char *text, bool bNew=true); - void GetText(char *buffer, int max); - char* GetText(); - int GetTextLength(); + void SetText(const std::string& text, bool bNew=true); + std::string GetText(int max); + const std::string& GetText(); + int GetTextLength(); - bool ReadText(std::string filename, int addSize=0); + bool ReadText(std::string filename); bool WriteText(std::string filename); void SetMaxChar(int max); @@ -234,8 +228,8 @@ protected: protected: std::unique_ptr m_scroll; // vertical scrollbar on the right - int m_maxChar; // max length of the buffer m_text - std::vector m_text; // text (without zero terminator) + int m_maxChar; + std::string m_text; // text (without zero terminator) std::vector m_format; // format characters int m_len; // length used in m_text int m_cursor1; // offset cursor @@ -256,14 +250,14 @@ protected: int m_lineVisible; // total number of viewable lines int m_lineFirst; // the first line displayed int m_lineTotal; // number lines used (in m_lineOffset) - int m_lineOffset[EDITLINEMAX]; - char m_lineIndent[EDITLINEMAX]; + std::vector m_lineOffset; + std::vector m_lineIndent; std::vector m_image; std::vector m_link; std::vector m_marker; int m_historyTotal; int m_historyCurrent; - HyperHistory m_history[EDITHISTORYMAX]; + std::array m_history; float m_time; // absolute time float m_timeBlink; float m_timeLastClick; @@ -276,7 +270,7 @@ protected: bool m_bUndoForce; OperUndo m_undoOper; - EditUndo m_undo[EDITUNDOMAX]; + std::array m_undo; }; diff --git a/src/ui/controls/editvalue.cpp b/src/ui/controls/editvalue.cpp index 21bb3d46..277f18b0 100644 --- a/src/ui/controls/editvalue.cpp +++ b/src/ui/controls/editvalue.cpp @@ -298,13 +298,13 @@ void CEditValue::SetValue(float value, bool bSendMessage) float CEditValue::GetValue() { - char text[100]; + std::string text; float value = 0.0f; if ( m_edit != nullptr ) { - m_edit->GetText(text, 100); - sscanf(text, "%f", &value); + text = m_edit->GetText(100); + sscanf(text.c_str(), "%f", &value); if ( m_type == EVT_100 ) { diff --git a/src/ui/screen/screen_io.cpp b/src/ui/screen/screen_io.cpp index 82db8d12..6cf1b4a9 100644 --- a/src/ui/screen/screen_io.cpp +++ b/src/ui/screen/screen_io.cpp @@ -193,11 +193,10 @@ void CScreenIO::IODeleteScene() } // clears filename only to leave letter or numbers -std::string clearName(char *name) +std::string clearName(std::string name) { std::string ret; - int len = strlen(name); - for (int i = 0; i < len; i++) + for (int i = 0; i < static_cast(name.size()); i++) { if (isalnum(name[i])) { @@ -214,7 +213,7 @@ void CScreenIO::IOWriteScene() CWindow* pw; CList* pl; CEdit* pe; - char info[100]; + std::string info; pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); if ( pw == nullptr ) return; @@ -226,7 +225,7 @@ void CScreenIO::IOWriteScene() int sel = pl->GetSelect(); if ( sel == -1 ) return; - pe->GetText(info, 100); + info = pe->GetText(100); m_interface->DeleteControl(EVENT_WINDOW5); diff --git a/src/ui/screen/screen_player_select.cpp b/src/ui/screen/screen_player_select.cpp index 678f8286..4bcd139b 100644 --- a/src/ui/screen/screen_player_select.cpp +++ b/src/ui/screen/screen_player_select.cpp @@ -232,7 +232,7 @@ void CScreenPlayerSelect::UpdateNameControl() CList* pl; CButton* pb; CEdit* pe; - char name[100]; + std::string name; int total, sel; pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); @@ -244,7 +244,7 @@ void CScreenPlayerSelect::UpdateNameControl() total = pl->GetTotal(); sel = pl->GetSelect(); - pe->GetText(name, 100); + name = pe->GetText(100); pb = static_cast(pw->SearchControl(EVENT_INTERFACE_NDELETE)); if ( pb != nullptr ) @@ -255,13 +255,13 @@ void CScreenPlayerSelect::UpdateNameControl() pb = static_cast(pw->SearchControl(EVENT_INTERFACE_NOK)); if ( pb != nullptr ) { - pb->SetState(STATE_ENABLE, name[0]!=0 || sel!=-1); + pb->SetState(STATE_ENABLE, !name.empty() || sel!=-1); } pb = static_cast(pw->SearchControl(EVENT_INTERFACE_PERSO)); if ( pb != nullptr ) { - pb->SetState(STATE_ENABLE, name[0]!=0 || sel!=-1); + pb->SetState(STATE_ENABLE, !name.empty() || sel!=-1); } } @@ -272,7 +272,7 @@ void CScreenPlayerSelect::UpdateNameList() CWindow* pw; CList* pl; CEdit* pe; - char name[100]; + std::string name; int total, i; pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); @@ -282,7 +282,7 @@ void CScreenPlayerSelect::UpdateNameList() pe = static_cast(pw->SearchControl(EVENT_INTERFACE_NEDIT)); if ( pe == nullptr ) return; - pe->GetText(name, 100); + name = pe->GetText(100); total = pl->GetTotal(); for ( i=0 ; i(m_interface->SearchControl(EVENT_WINDOW5)); @@ -349,7 +349,7 @@ void CScreenPlayerSelect::NameSelect() pe = static_cast(pw->SearchControl(EVENT_INTERFACE_NEDIT)); if ( pe == nullptr ) return; - pe->GetText(name, 100); + name = pe->GetText(100); sel = pl->GetSelect(); if ( sel == -1 ) @@ -377,9 +377,9 @@ bool CScreenPlayerSelect::NameCreate() pe = static_cast(pw->SearchControl(EVENT_INTERFACE_NEDIT)); if ( pe == nullptr ) return false; - char name[100]; - pe->GetText(name, 100); - if ( name[0] == 0 ) + std::string name; + name = pe->GetText(100); + if ( name.empty() ) { m_sound->Play(SOUND_TZOING); return false; diff --git a/src/ui/studio.cpp b/src/ui/studio.cpp index 22268ecb..4f6fa206 100644 --- a/src/ui/studio.cpp +++ b/src/ui/studio.cpp @@ -436,8 +436,8 @@ void CStudio::SearchToken(CEdit* edit) { ObjectType type; int len, cursor1, cursor2, i, character, level; - char* text; - char token[100]; + std::string text; + std::string token( 100, '\0'); text = edit->GetText(); len = edit->GetTextLength(); @@ -503,7 +503,7 @@ void CStudio::SearchToken(CEdit* edit) } token[i] = 0; - m_helpFilename = GetHelpFilename(token); + m_helpFilename = GetHelpFilename(token.c_str()); if ( m_helpFilename.length() == 0 ) { for ( i=0 ; i(GetObjectName(type)); if ( text[0] != 0 ) { - if ( strcmp(token, text) == 0 ) + if ( token == text ) { m_helpFilename = GetHelpFilename(type); - SetInfoText(std::string(token), true); + SetInfoText(token, true); return; } } text = const_cast(GetObjectAlias(type)); if ( text[0] != 0 ) { - if ( strcmp(token, text) == 0 ) + if ( token == text ) { m_helpFilename = GetHelpFilename(type); - SetInfoText(std::string(token), true); + SetInfoText(token, true); return; } } } } - text = const_cast(GetHelpText(token)); + text = const_cast(GetHelpText(token.c_str())); if ( text[0] == 0 && m_helpFilename.length() > 0 ) { - SetInfoText(std::string(token), true); + SetInfoText(token, true); } else { @@ -606,7 +606,6 @@ void CStudio::StartEditScript(CScript *script, std::string name, Program* progra edit->SetState(STATE_SHADOW); edit->SetInsideScroll(false); //? if ( m_bRunning ) edit->SetEdit(false); - edit->SetMaxChar(EDITSTUDIOMAX); edit->SetFontType(Gfx::FONT_COURIER); edit->SetFontStretch(1.0f); edit->SetDisplaySpec(true); @@ -1245,7 +1244,7 @@ void CStudio::AdjustDialog() CEdit* pe; Math::Point wpos, wdim, ppos, ddim; int nli, nch; - char name[100]; + std::string name; pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW9)); if ( pw == nullptr ) return; @@ -1302,7 +1301,7 @@ void CStudio::AdjustDialog() pe->SetDim(ddim); nch = static_cast< int >((ddim.x*640.0f-22.0f)/8.0f); - pe->GetText(name, 100); + name = pe->GetText(100); pe->SetMaxChar(nch); name[nch] = 0; // truncates the text according to max pe->SetText(name); @@ -1467,7 +1466,7 @@ void CStudio::SetFilenameField(CEdit* edit, const std::string& filename) name = name.substr(0, edit->GetMaxChar()); // truncates according to max length } } - edit->SetText(name.c_str()); + edit->SetText(name); } // Updates the list after a change in name. @@ -1494,7 +1493,7 @@ void CStudio::UpdateDialogAction() CWindow* pw; CEdit* pe; CButton* pb; - char name[100]; + std::string name; int len, i; bool bError; @@ -1505,8 +1504,8 @@ void CStudio::UpdateDialogAction() pb = static_cast< CButton* >(pw->SearchControl(EVENT_DIALOG_OK)); if ( pb == nullptr ) return; - pe->GetText(name, 100); - len = strlen(name); + name = pe->GetText(100); + len = name.size(); if ( len == 0 ) { bError = true; @@ -1558,7 +1557,7 @@ void CStudio::UpdateDialogPublic() if ( pl != nullptr ) { // GetResource(RES_TEXT, RT_IO_LIST, name); // TODO: unused? - pl->SetName(SearchDirectory(false).c_str(), false); + pl->SetName(SearchDirectory(false), false); } } @@ -1619,25 +1618,25 @@ bool CStudio::ReadProgram() { CWindow* pw; CEdit* pe; - char filename[100]; - char dir[100]; - char* p; + std::string filename; + std::string dir; + size_t p; pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW9)); if ( pw == nullptr ) return false; pe = static_cast< CEdit* >(pw->SearchControl(EVENT_DIALOG_EDIT)); if ( pe == nullptr ) return false; - pe->GetText(filename, 100); - if ( filename[0] == 0 ) return false; + filename = pe->GetText(100); + if ( filename.empty() ) return false; - p = strstr(filename, ".txt"); - if ( p == nullptr || p != filename+strlen(filename)-4 ) + p = filename.find(".txt"); + if ( p == std::string::npos ) { - strcat(filename, ".txt"); + filename += ".txt"; } - strcpy(dir, SearchDirectory(true).c_str()); - strcat(dir, filename); + dir = SearchDirectory(true); + dir += filename; pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW3)); if ( pw == nullptr ) return false; @@ -1657,32 +1656,32 @@ bool CStudio::WriteProgram() { CWindow* pw; CEdit* pe; - char filename[100]; - char dir[100]; - char* p; + std::string filename; + std::string dir; + size_t p; pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW9)); if ( pw == nullptr ) return false; pe = static_cast< CEdit* >(pw->SearchControl(EVENT_DIALOG_EDIT)); if ( pe == nullptr ) return false; - pe->GetText(filename, 100); - if ( filename[0] == 0 ) return false; + filename = pe->GetText(100); + if ( filename.empty() ) return false; - p = strstr(filename, ".txt"); - if ( p == nullptr || p != filename+strlen(filename)-4 ) + p = filename.find(".txt"); + if ( p == std::string::npos ) { - strcat(filename, ".txt"); + filename += ".txt"; } - strcpy(dir, SearchDirectory(true).c_str()); - strcat(dir, filename); + dir = SearchDirectory(true); + dir += filename; pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW3)); if ( pw == nullptr ) return false; pe = static_cast< CEdit* >(pw->SearchControl(EVENT_STUDIO_EDIT)); if ( pe == nullptr ) return false; - if ( !pe->WriteText(std::string(dir)) ) return false; + if ( !pe->WriteText(dir) ) return false; m_script->SetFilename(filename); return true; From 3472ec661307afd686e3ba700e876d78b19c262a Mon Sep 17 00:00:00 2001 From: krzys-h Date: Tue, 27 Sep 2016 18:32:21 +0200 Subject: [PATCH 46/56] Revert CParticle::CheckChannel changes I misinterpreted this as being a bug, while actually it seems to be an explicit CParticle design choice (maybe not the best one, but whatever). We DO need better docs for some old code like this :/ Fixes #806 This reverts commit 99a831a03be068dd0c40ee3a7ca162cdcfb5e85d. --- src/graphics/engine/particle.cpp | 56 +++++++++++++++++--------------- src/graphics/engine/particle.h | 13 +++----- src/object/old_object.cpp | 6 ++-- src/object/old_object.h | 6 ++-- 4 files changed, 39 insertions(+), 42 deletions(-) diff --git a/src/graphics/engine/particle.cpp b/src/graphics/engine/particle.cpp index 5f8b1601..5aaf45fa 100644 --- a/src/graphics/engine/particle.cpp +++ b/src/graphics/engine/particle.cpp @@ -581,7 +581,7 @@ int CParticle::CreateTrack(Math::Vector pos, Math::Vector speed, Math::Point dim if (!m_track[i].used) // free? { int rank = channel; - GetRankFromChannel(rank); + if (!CheckChannel(rank)) return -1; m_particle[rank].trackRank = i; m_track[i].used = true; @@ -636,27 +636,28 @@ void CParticle::CreateWheelTrace(const Math::Vector &p1, const Math::Vector &p2, -void CParticle::GetRankFromChannel(int &channel) +/** Adapts the channel so it can be used as an offset in m_particle */ +bool CParticle::CheckChannel(int &channel) { int uniqueStamp = (channel>>16)&0xffff; channel &= 0xffff; - if (channel < 0 || channel >= MAXPARTICULE*MAXPARTITYPE) throw std::runtime_error("Tried to access invalid particle channel (invalid ID)"); - if (!m_particle[channel].used) throw std::runtime_error("Tried to access invalid particle channel (used=false)"); - if (m_particle[channel].uniqueStamp != uniqueStamp) throw std::runtime_error("Tried to access invalid particle channel (uniqueStamp changed)"); -} + if (channel < 0) return false; + if (channel >= MAXPARTICULE*MAXPARTITYPE) return false; -bool CParticle::ParticleExists(int channel) -{ - try - { - GetRankFromChannel(channel); - return true; - } - catch (const std::runtime_error& e) + if (!m_particle[channel].used) { + GetLogger()->Trace("Particle %d:%d doesn't exist anymore (used=false)\n", channel, uniqueStamp); return false; } + + if (m_particle[channel].uniqueStamp != uniqueStamp) + { + GetLogger()->Trace("Particle %d:%d doesn't exist anymore (uniqueStamp changed)\n", channel, uniqueStamp); + return false; + } + + return true; } void CParticle::DeleteRank(int rank) @@ -684,7 +685,7 @@ void CParticle::DeleteParticle(ParticleType type) void CParticle::DeleteParticle(int channel) { - GetRankFromChannel(channel); + if (!CheckChannel(channel)) return; if (m_totalInterface[channel/MAXPARTICULE][m_particle[channel].sheet] > 0 ) m_totalInterface[channel/MAXPARTICULE][m_particle[channel].sheet]--; @@ -698,50 +699,50 @@ void CParticle::DeleteParticle(int channel) void CParticle::SetObjectLink(int channel, CObject *object) { - GetRankFromChannel(channel); + if (!CheckChannel(channel)) return; m_particle[channel].objLink = object; } void CParticle::SetObjectFather(int channel, CObject *object) { - GetRankFromChannel(channel); + if (!CheckChannel(channel)) return; m_particle[channel].objFather = object; } void CParticle::SetPosition(int channel, Math::Vector pos) { - GetRankFromChannel(channel); + if (!CheckChannel(channel)) return; m_particle[channel].pos = pos; } void CParticle::SetDimension(int channel, Math::Point dim) { - GetRankFromChannel(channel); + if (!CheckChannel(channel)) return; m_particle[channel].dim = dim; } void CParticle::SetZoom(int channel, float zoom) { - GetRankFromChannel(channel); + if (!CheckChannel(channel)) return; m_particle[channel].zoom = zoom; } void CParticle::SetAngle(int channel, float angle) { - GetRankFromChannel(channel); + if (!CheckChannel(channel)) return; m_particle[channel].angle = angle; } void CParticle::SetIntensity(int channel, float intensity) { - GetRankFromChannel(channel); + if (!CheckChannel(channel)) return; m_particle[channel].intensity = intensity; } void CParticle::SetParam(int channel, Math::Vector pos, Math::Point dim, float zoom, float angle, float intensity) { - GetRankFromChannel(channel); + if (!CheckChannel(channel)) return; m_particle[channel].pos = pos; m_particle[channel].dim = dim; m_particle[channel].zoom = zoom; @@ -751,16 +752,17 @@ void CParticle::SetParam(int channel, Math::Vector pos, Math::Point dim, float z void CParticle::SetPhase(int channel, ParticlePhase phase, float duration) { - GetRankFromChannel(channel); + if (!CheckChannel(channel)) return; m_particle[channel].phase = phase; m_particle[channel].duration = duration; m_particle[channel].phaseTime = m_particle[channel].time; } -Math::Vector CParticle::GetPosition(int channel) +bool CParticle::GetPosition(int channel, Math::Vector &pos) { - GetRankFromChannel(channel); - return m_particle[channel].pos; + if (!CheckChannel(channel)) return false; + pos = m_particle[channel].pos; + return true; } void CParticle::SetFrameUpdate(int sheet, bool update) diff --git a/src/graphics/engine/particle.h b/src/graphics/engine/particle.h index ec8d8820..03707a9c 100644 --- a/src/graphics/engine/particle.h +++ b/src/graphics/engine/particle.h @@ -162,9 +162,9 @@ enum ParticlePhase struct Particle { - bool used = false; //!< true if this channel is used, false if not + bool used = false; // TRUE -> particle used bool ray = false; // TRUE -> ray with goal - unsigned short uniqueStamp = 0; //!< unique marker added to particle channel ID to make sure this is still the same particle + unsigned short uniqueStamp = 0; // unique mark short sheet = 0; // sheet (0..n) ParticleType type = {}; // type PARTI* ParticlePhase phase = {}; // phase PARPH* @@ -279,7 +279,7 @@ public: void SetPhase(int channel, ParticlePhase phase, float duration); //! Returns the position of the particle - Math::Vector GetPosition(int channel); + bool GetPosition(int channel, Math::Vector &pos); //! Returns the color if you're in the fog or black if you're not Color GetFogColor(Math::Vector pos); @@ -291,18 +291,15 @@ public: //! Draws all the particles void DrawParticle(int sheet); - //! Checks if given particle channel still exists - bool ParticleExists(int channel); - protected: //! Removes a particle of given rank void DeleteRank(int rank); /** * \brief Adapts the channel so it can be used as an offset in m_particle * \param channel Channel number to process, will be modified to be index of particle in m_particle - * \throw std::runtime_error if this particle does not exist any more + * \return true if success, false if particle doesn't exist anymore **/ - void GetRankFromChannel(int &channel); + bool CheckChannel(int &channel); //! Draws a triangular particle void DrawParticleTriangle(int i); //! Draw a normal particle diff --git a/src/object/old_object.cpp b/src/object/old_object.cpp index 32861cca..c8937423 100644 --- a/src/object/old_object.cpp +++ b/src/object/old_object.cpp @@ -2185,17 +2185,15 @@ void COldObject::PartiFrame(float rTime) channel = m_objectPart[i].masterParti; if ( channel == -1 ) continue; - if ( !m_particle->ParticleExists(channel) ) + if ( !m_particle->GetPosition(channel, pos) ) { m_objectPart[i].masterParti = -1; // particle no longer exists! continue; } - pos = m_particle->GetPosition(channel); - SetPartPosition(i, pos); - // Each part rotates differently + // Each song spins differently. switch( i%5 ) { case 0: factor = Math::Vector( 0.5f, 0.3f, 0.6f); break; diff --git a/src/object/old_object.h b/src/object/old_object.h index 60e8e8fa..5b85c286 100644 --- a/src/object/old_object.h +++ b/src/object/old_object.h @@ -55,9 +55,9 @@ const int OBJECTMAXPART = 40; struct ObjectPart { bool bUsed = false; - int object = -1; //!< identifier of the object in Gfx::CEngine - int parentPart = -1; //!< identifier of parent part - int masterParti = -1; //!< particle channel this part is connected to after explosion + int object = -1; // number of the object in CEngine + int parentPart = -1; // number of father part + int masterParti = -1; // master canal of the particle Math::Vector position; Math::Vector angle; Math::Vector zoom; From 73be03161fc07678b65d27450763c1b733a4ae0a Mon Sep 17 00:00:00 2001 From: krzys-h Date: Tue, 27 Sep 2016 18:57:24 +0200 Subject: [PATCH 47/56] Fix infinite pause when cloning program with errors, closes #837 --- src/ui/object_interface.cpp | 8 ++++---- src/ui/object_interface.h | 13 ++++++++++++- src/ui/studio.cpp | 4 ++-- src/ui/studio.h | 2 +- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/ui/object_interface.cpp b/src/ui/object_interface.cpp index 44140059..a435c825 100644 --- a/src/ui/object_interface.cpp +++ b/src/ui/object_interface.cpp @@ -275,7 +275,7 @@ bool CObjectInterface::EventProcess(const Event &event) } if( action == EVENT_STUDIO_CLONE ) { - StopEditScript(false); + StopEditScript(true); Program* newProgram = m_programStorage->CloneProgram(m_programStorage->GetProgram(m_selScript)); m_selScript = m_programStorage->GetProgramIndex(newProgram); m_main->SaveOneScript(m_object); @@ -738,12 +738,12 @@ void CObjectInterface::StartEditScript(Program* program, std::string name) // End of editing a program. -void CObjectInterface::StopEditScript(bool bCancel) +void CObjectInterface::StopEditScript(bool closeWithErrors) { - if ( !m_studio->StopEditScript(bCancel) ) return; + if ( !m_studio->StopEditScript(closeWithErrors) ) return; m_studio.reset(); - if ( !bCancel ) m_programStorage->SetActiveVirus(false); + if ( !closeWithErrors ) m_programStorage->SetActiveVirus(false); CreateInterface(true); // puts the control buttons } diff --git a/src/ui/object_interface.h b/src/ui/object_interface.h index b7b97105..a35638a4 100644 --- a/src/ui/object_interface.h +++ b/src/ui/object_interface.h @@ -64,8 +64,19 @@ public: protected: bool EventFrame(const Event &event); + /** + * \brief Start the program editor + * \param program Program instance to edit + * \param name Program name, used only if the program is empty to insert main function stub code + * \remarks If the editor is already open, the behaviour is undefined + */ void StartEditScript(Program* program, std::string name); - void StopEditScript(bool bCancel); + /** + * \brief Close the program editor + * \param closeWithErrors If true, the editor will be forced to close even if the program contains errors + * \remarks If the editor isn't open, the behaviour is undefined + */ + void StopEditScript(bool closeWithErrors); void GroundFlat(); void ColorFlag(int color); diff --git a/src/ui/studio.cpp b/src/ui/studio.cpp index 4f6fa206..e3545cbb 100644 --- a/src/ui/studio.cpp +++ b/src/ui/studio.cpp @@ -875,7 +875,7 @@ void CStudio::AdjustEditScript() // Ends edition of a program. -bool CStudio::StopEditScript(bool bCancel) +bool CStudio::StopEditScript(bool closeWithErrors) { CWindow* pw; CEdit* edit; @@ -883,7 +883,7 @@ bool CStudio::StopEditScript(bool bCancel) pw = static_cast< CWindow* >(m_interface->SearchControl(EVENT_WINDOW3)); if ( pw == nullptr ) return false; - if ( !bCancel && !m_script->IsRunning() ) + if ( !closeWithErrors && !m_script->IsRunning() ) { edit = static_cast< CEdit* >(pw->SearchControl(EVENT_STUDIO_EDIT)); if ( edit != nullptr ) diff --git a/src/ui/studio.h b/src/ui/studio.h index 201e58b2..f3369754 100644 --- a/src/ui/studio.h +++ b/src/ui/studio.h @@ -58,7 +58,7 @@ public: bool EventProcess(const Event &event); void StartEditScript(CScript *script, std::string name, Program* program); - bool StopEditScript(bool bCancel); + bool StopEditScript(bool closeWithErrors); void SetInfoText(std::string text, bool bClickable); From d10760500741edbb51f822194e1d200d8a1cd9a4 Mon Sep 17 00:00:00 2001 From: MatiRg Date: Fri, 30 Sep 2016 17:03:13 +0200 Subject: [PATCH 48/56] Add file append mode in CBot (#838) --- src/CBot/stdlib/FileFunctions.cpp | 10 ++++++++-- src/CBot/stdlib/stdlib_public.h | 2 +- src/common/resources/outputstream.cpp | 8 ++++---- src/common/resources/outputstream.h | 17 +++++++++++++++-- src/common/resources/outputstreambuffer.cpp | 7 +++++-- src/common/resources/outputstreambuffer.h | 8 +++++++- src/script/scriptfunc.cpp | 8 ++++++++ 7 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/CBot/stdlib/FileFunctions.cpp b/src/CBot/stdlib/FileFunctions.cpp index 1a06b18d..4f2f6134 100644 --- a/src/CBot/stdlib/FileFunctions.cpp +++ b/src/CBot/stdlib/FileFunctions.cpp @@ -51,7 +51,7 @@ bool FileClassOpenFile(CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exc { // recover mode mode = pVar->GetValString(); - if ( mode != "r" && mode != "w" ) { Exception = CBotErrBadParam; return false; } + if ( mode != "r" && mode != "w" && mode != "a" ) { Exception = CBotErrBadParam; return false; } // no third parameter if ( pVar->GetNext() != nullptr ) { Exception = CBotErrOverParam; return false; } @@ -70,7 +70,13 @@ bool FileClassOpenFile(CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exc { // opens the requested file assert(g_fileHandler != nullptr); - std::unique_ptr file = g_fileHandler->OpenFile(filename, mode == "r" ? CBotFileAccessHandler::OpenMode::Read : CBotFileAccessHandler::OpenMode::Write); + + CBotFileAccessHandler::OpenMode openMode; + if ( mode == "r" ) openMode = CBotFileAccessHandler::OpenMode::Read; + else if ( mode == "w" ) openMode = CBotFileAccessHandler::OpenMode::Write; + else if ( mode == "a" ) openMode = CBotFileAccessHandler::OpenMode::Append; + + std::unique_ptr file = g_fileHandler->OpenFile(filename, openMode); if (!file->Opened()) { Exception = CBotErrFileOpen; return false; } diff --git a/src/CBot/stdlib/stdlib_public.h b/src/CBot/stdlib/stdlib_public.h index d1da95ca..bb5fc337 100644 --- a/src/CBot/stdlib/stdlib_public.h +++ b/src/CBot/stdlib/stdlib_public.h @@ -44,7 +44,7 @@ class CBotFileAccessHandler public: virtual ~CBotFileAccessHandler() {} - enum class OpenMode : char { Read = 'r', Write = 'w' }; + enum class OpenMode : char { Read = 'r', Write = 'w', Append = 'a' }; virtual std::unique_ptr OpenFile(const std::string& filename, OpenMode mode) = 0; virtual bool DeleteFile(const std::string& filename) = 0; }; diff --git a/src/common/resources/outputstream.cpp b/src/common/resources/outputstream.cpp index eceeef86..0d43beb5 100644 --- a/src/common/resources/outputstream.cpp +++ b/src/common/resources/outputstream.cpp @@ -28,20 +28,20 @@ COutputStream::COutputStream() { } -COutputStream::COutputStream(const std::string& filename) +COutputStream::COutputStream(const std::string& filename, std::ios_base::openmode mode) : COutputStreamBufferContainer(), std::ostream(&m_buffer) { - open(filename); + open(filename, mode); } COutputStream::~COutputStream() { } -void COutputStream::open(const std::string& filename) +void COutputStream::open(const std::string& filename, std::ios_base::openmode mode) { - m_buffer.open(filename); + m_buffer.open(filename, mode); } void COutputStream::close() diff --git a/src/common/resources/outputstream.h b/src/common/resources/outputstream.h index 31a0e9f1..c1ac33d4 100644 --- a/src/common/resources/outputstream.h +++ b/src/common/resources/outputstream.h @@ -35,10 +35,23 @@ class COutputStream : public COutputStreamBufferContainer, public std::ostream { public: COutputStream(); - COutputStream(const std::string& filename); + + /** Construct and Open Stream for writing + * + * \param filename + * \param Mode one of: std::ios_base::out - Open for writing, std::ios_base::app - Append to file + * + */ + COutputStream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out); virtual ~COutputStream(); - void open(const std::string& filename); + /** Open Stream for writing + * + * \param filename + * \param Mode one of: std::ios_base::out - Open for writing, std::ios_base::app - Append to file + * + */ + void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out); void close(); bool is_open(); }; diff --git a/src/common/resources/outputstreambuffer.cpp b/src/common/resources/outputstreambuffer.cpp index 0edb7b77..27952388 100644 --- a/src/common/resources/outputstreambuffer.cpp +++ b/src/common/resources/outputstreambuffer.cpp @@ -45,10 +45,13 @@ COutputStreamBuffer::~COutputStreamBuffer() } -void COutputStreamBuffer::open(const std::string &filename) +void COutputStreamBuffer::open(const std::string &filename, std::ios_base::openmode mode) { if (PHYSFS_isInit()) - m_file = PHYSFS_openWrite(CResourceManager::CleanPath(filename).c_str()); + { + if ( mode == std::ios_base::out ) m_file = PHYSFS_openWrite(CResourceManager::CleanPath(filename).c_str()); + else if ( mode == std::ios_base::app ) m_file = PHYSFS_openAppend(CResourceManager::CleanPath(filename).c_str()); + } } diff --git a/src/common/resources/outputstreambuffer.h b/src/common/resources/outputstreambuffer.h index ed5eabb2..fb7fa538 100644 --- a/src/common/resources/outputstreambuffer.h +++ b/src/common/resources/outputstreambuffer.h @@ -35,7 +35,13 @@ public: COutputStreamBuffer(const COutputStreamBuffer &) = delete; COutputStreamBuffer &operator= (const COutputStreamBuffer &) = delete; - void open(const std::string &filename); + /** Open Stream Buffer for writing + * + * \param filename + * \param Mode one of: std::ios_base::out - Open for writing, std::ios_base::app - Append to file + * + */ + void open(const std::string &filename, std::ios_base::openmode mode); void close(); bool is_open(); diff --git a/src/script/scriptfunc.cpp b/src/script/scriptfunc.cpp index e4da1f16..c3e57555 100644 --- a/src/script/scriptfunc.cpp +++ b/src/script/scriptfunc.cpp @@ -3000,6 +3000,14 @@ public: m_file = std::move(os); } } + else if (mode == CBotFileAccessHandler::OpenMode::Append) + { + auto os = MakeUnique(filename, std::ios_base::app); + if (os->is_open()) + { + m_file = std::move(os); + } + } if (Opened()) { From a96835e35f086b2560ea8f64af45043d48a02b6c Mon Sep 17 00:00:00 2001 From: krzys-h Date: Fri, 30 Sep 2016 17:08:37 +0200 Subject: [PATCH 49/56] Change CBot file log messages Open mode is logged now. Additionally fixed incorrect file path in log when deleting files. --- src/script/scriptfunc.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/script/scriptfunc.cpp b/src/script/scriptfunc.cpp index c3e57555..7f6cb97b 100644 --- a/src/script/scriptfunc.cpp +++ b/src/script/scriptfunc.cpp @@ -3011,7 +3011,7 @@ public: if (Opened()) { - GetLogger()->Info("CBot open file '%s'\n", filename.c_str()); + GetLogger()->Info("CBot open file '%s', mode '%c'\n", filename.c_str(), mode); m_numFilesOpen++; } } @@ -3020,7 +3020,7 @@ public: { if (Opened()) { - GetLogger()->Debug("CBot close file\n"); + GetLogger()->Info("CBot close file\n"); m_numFilesOpen--; } @@ -3079,8 +3079,9 @@ public: virtual bool DeleteFile(const std::string& filename) override { - GetLogger()->Info("CBot delete file '%s'\n", filename.c_str()); - return CResourceManager::Remove(PrepareFilename(filename)); + std::string fname = PrepareFilename(filename); + GetLogger()->Info("CBot delete file '%s'\n", fname.c_str()); + return CResourceManager::Remove(fname); } private: From bb9d1c82655af26f13cbf5dc094a8bd7f0f351a9 Mon Sep 17 00:00:00 2001 From: krzys-h Date: Sun, 2 Oct 2016 21:36:59 +0200 Subject: [PATCH 50/56] Remove "this == nullptr" checks in CBOT, fixes #828 --- src/CBot/CBotClass.cpp | 6 +++--- src/CBot/CBotProgram.cpp | 21 ++++++++++++++------- src/CBot/CBotStack.cpp | 14 +++++++------- src/CBot/CBotVar/CBotVarClass.cpp | 5 ++--- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/CBot/CBotClass.cpp b/src/CBot/CBotClass.cpp index 460ab9fa..8efc4af8 100644 --- a/src/CBot/CBotClass.cpp +++ b/src/CBot/CBotClass.cpp @@ -92,7 +92,7 @@ void CBotClass::ClearPublic() //////////////////////////////////////////////////////////////////////////////// void CBotClass::Purge() { - if ( this == nullptr ) return; + assert ( this != nullptr ); delete m_pVar; m_pVar = nullptr; @@ -104,7 +104,7 @@ void CBotClass::Purge() m_nbVar = m_parent == nullptr ? 0 : m_parent->m_nbVar; - m_next->Purge(); + if (m_next != nullptr) m_next->Purge(); m_next = nullptr; // no longer belongs to this chain } @@ -205,7 +205,7 @@ std::string CBotClass::GetName() //////////////////////////////////////////////////////////////////////////////// CBotClass* CBotClass::GetParent() { - if ( this == nullptr ) return nullptr; + assert ( this != nullptr ); return m_parent; } diff --git a/src/CBot/CBotProgram.cpp b/src/CBot/CBotProgram.cpp index 81008297..17c5ec0a 100644 --- a/src/CBot/CBotProgram.cpp +++ b/src/CBot/CBotProgram.cpp @@ -47,13 +47,13 @@ CBotProgram::CBotProgram(CBotVar* thisVar) CBotProgram::~CBotProgram() { // delete m_classes; - m_classes->Purge(); + if (m_classes != nullptr) m_classes->Purge(); m_classes = nullptr; CBotClass::FreeLock(this); delete m_functions; - m_stack->Delete(); + if (m_stack != nullptr) m_stack->Delete(); } bool CBotProgram::Compile(const std::string& program, std::vector& functions, void* pUser) @@ -62,7 +62,7 @@ bool CBotProgram::Compile(const std::string& program, std::vector& Stop(); // delete m_classes; - m_classes->Purge(); // purge the old definitions of classes + if (m_classes != nullptr) m_classes->Purge(); // purge the old definitions of classes // but without destroying the object m_classes = nullptr; delete m_functions; m_functions = nullptr; @@ -227,8 +227,11 @@ bool CBotProgram::Run(void* pUser, int timer) void CBotProgram::Stop() { - m_stack->Delete(); - m_stack = nullptr; + if (m_stack != nullptr) + { + m_stack->Delete(); + m_stack = nullptr; + } m_entryPoint = nullptr; CBotClass::FreeLock(this); } @@ -365,11 +368,15 @@ bool CBotProgram::RestoreState(FILE* pf) if (!ReadString( pf, s )) return false; Start(s); // point de reprise - m_stack->Delete(); - m_stack = nullptr; + if (m_stack != nullptr) + { + m_stack->Delete(); + m_stack = nullptr; + } // retrieves the stack from the memory // uses a nullptr pointer (m_stack) but it's ok like that + // TODO: no it's not okay like that! but it looks like it doesn't get optimized out at least ~krzys_h if (!m_stack->RestoreState(pf, m_stack)) return false; m_stack->SetProgram(this); // bases for routines diff --git a/src/CBot/CBotStack.cpp b/src/CBot/CBotStack.cpp index 295e6063..94697b4a 100644 --- a/src/CBot/CBotStack.cpp +++ b/src/CBot/CBotStack.cpp @@ -82,10 +82,10 @@ CBotStack* CBotStack::AllocateStack() //////////////////////////////////////////////////////////////////////////////// void CBotStack::Delete() { - if ( this == nullptr ) return; + assert ( this != nullptr ); - m_next->Delete(); - m_next2->Delete(); + if (m_next != nullptr) m_next->Delete(); + if (m_next2 != nullptr) m_next2->Delete(); if (m_prev != nullptr) { @@ -192,8 +192,8 @@ bool CBotStack::Return(CBotStack* pfils) m_var = pfils->m_var; // result transmitted pfils->m_var = nullptr; // not to destroy the variable - m_next->Delete();m_next = nullptr; // releases the stack above - m_next2->Delete();m_next2 = nullptr; // also the second stack (catch) + if (m_next != nullptr) m_next->Delete();m_next = nullptr; // releases the stack above + if (m_next2 != nullptr) m_next2->Delete();m_next2 = nullptr; // also the second stack (catch) return IsOk(); // interrupted if error } @@ -281,7 +281,7 @@ bool CBotStack::IfContinue(int state, const std::string& name) m_state = state; // where again? m_error = CBotNoErr; m_labelBreak.clear(); - m_next->Delete(); // purge above stack + if (m_next != nullptr) m_next->Delete(); // purge above stack return true; } @@ -478,7 +478,7 @@ bool CBotStack::Execute() if (!instr->Run(nullptr, pile)) return false; // resume interrupted execution - pile->m_next->Delete(); + if (pile->m_next != nullptr) pile->m_next->Delete(); pile->m_callFinished = true; return true; diff --git a/src/CBot/CBotVar/CBotVarClass.cpp b/src/CBot/CBotVar/CBotVarClass.cpp index 08acffbe..4da99b2d 100644 --- a/src/CBot/CBotVar/CBotVarClass.cpp +++ b/src/CBot/CBotVar/CBotVarClass.cpp @@ -69,11 +69,10 @@ CBotVarClass::CBotVarClass(const CBotToken& name, const CBotTypResult& type) m_instances.insert(this); CBotClass* pClass = type.GetClass(); - CBotClass* pClass2 = pClass->GetParent(); - if ( pClass2 != nullptr ) + if ( pClass != nullptr && pClass->GetParent() != nullptr ) { // also creates an instance of the parent class - m_pParent = new CBotVarClass(name, CBotTypResult(type.GetType(),pClass2) ); //, nIdent); + m_pParent = new CBotVarClass(name, CBotTypResult(type.GetType(), pClass->GetParent()) ); //, nIdent); } SetClass( pClass ); From fee1619c9cc0aa6e98ee2a58fada8f61c4853786 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Mon, 24 Oct 2016 23:16:10 +0200 Subject: [PATCH 51/56] Update data submodule --- data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data b/data index 2b7248ab..1a6df3dc 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 2b7248ab617474f7dd52d05c9a7dfec7ccad6e30 +Subproject commit 1a6df3dca9178c5d135015a2be4ce0b1de6fb32b From ef28b0dd14448ae35bde416288e7534eb0c37658 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Tue, 1 Nov 2016 19:36:11 +0100 Subject: [PATCH 52/56] Update data submodule --- data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data b/data index 1a6df3dc..bc82bbe2 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 1a6df3dca9178c5d135015a2be4ce0b1de6fb32b +Subproject commit bc82bbe2f06f11b2c70d19a3c26ce915c9211d23 From 81f4436951349585a5a81c70ac53acef2c112684 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Wed, 2 Nov 2016 20:17:36 +0100 Subject: [PATCH 53/56] Update data submodule --- data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data b/data index bc82bbe2..27d3674c 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit bc82bbe2f06f11b2c70d19a3c26ce915c9211d23 +Subproject commit 27d3674c6809b4c466e7cc81a7942a94c5280698 From 3c1bf3c0145d2caf173b45fa2039b6a20d987554 Mon Sep 17 00:00:00 2001 From: krzys-h Date: Wed, 2 Nov 2016 21:31:34 +0100 Subject: [PATCH 54/56] Added script for use when releasing new versions --- tools/release.py | 104 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 tools/release.py diff --git a/tools/release.py b/tools/release.py new file mode 100644 index 00000000..526e41a4 --- /dev/null +++ b/tools/release.py @@ -0,0 +1,104 @@ +from __future__ import print_function +import fileinput +import re +import os + +# Script to use when releasing new versions +# Run from main repo directory with data submodule pulled in data/ +# +# Note: this has not yet been tested thoughtly, VERIFY EVERYTHING THIS SCRIPT DOES MANUALLY +# +# Will automatically: +# * Make sure you don't forget to pull any changes before you start +# * Get current version number from CMakeLists.txt +# * Merge dev -> master in the data submodule +# * Tag release in data submodule +# * Update dev in data submodule to point to the new merge commit +# * Merge dev -> master in main repo +# * Bump version number in main repo +# * Tag release in main repo +# * Update dev in main repo to point to the new merge commit +# +# After finished, verify everything is correct and push the changes + +print('\033[1;34m[*] Make sure all remote changes are pulled...\033[0m') +os.system('git checkout dev') +os.system('git pull --ff') +os.system('git checkout master') +os.system('git pull --ff') +os.chdir('data') +os.system('git checkout dev') +os.system('git pull --ff') +os.system('git checkout master') +os.system('git pull --ff') +os.chdir('..') + +print('\033[1;34m[*] Get version numbers...\033[0m') +os.system('git checkout dev') +major = None +minor = None +revision = None +codename = None +data = open('CMakeLists.txt', 'r').readlines() + +for i in range(len(data)): + m = re.match(r'^set\(COLOBOT_VERSION_(MAJOR|MINOR|REVISION)( +)([0-9])+\)$', data[i]) + if m: + x = int(m.group(3)) + if m.group(1) == 'MAJOR': + major = x + elif m.group(1) == 'MINOR': + minor = x + elif m.group(1) == 'REVISION': + # Increase revision number + x += 1 + revision = x + data[i] = 'set(COLOBOT_VERSION_'+m.group(1)+m.group(2)+str(x)+')\n' + + m = re.match(r'^(#?)set\(COLOBOT_VERSION_(UNRELEASED|RELEASE_CODENAME)( +)"(.+)"\)$', data[i]) + if m: + comment = (m.group(2) == 'UNRELEASED') + if m.group(2) == 'RELEASE_CODENAME': + codename = m.group(4) + data[i] = ('#' if comment else '')+'set(COLOBOT_VERSION_'+m.group(2)+m.group(3)+'"'+m.group(4)+'")\n' + +os.system('git checkout master') +version = '%d.%d.%d%s' % (major, minor, revision, codename) +print('\033[1;32m[+] Building version '+version+'\033[0m') + +print('\033[1;34m[*] Merge data...\033[0m') +os.chdir('data') +os.system('git merge dev --no-ff -m "Release '+version+': Merge branch \'dev\'"') + +print('\033[1;34m[*] Tag data...\033[0m') +os.system('git tag colobot-gold-'+version) + +print('\033[1;34m[*] Update dev on data...\033[0m') +os.system('git checkout dev') +os.system('git merge master --ff') +os.system('git checkout master') + +print('\033[1;34m[*] Merge main...\033[0m') +os.chdir('..') +os.system('git merge dev --no-ff -m "Release '+version+': Merge branch \'dev\'"') + +print('\033[1;34m[*] Bump version number\033[0m') +open('CMakeLists.txt', 'w').writelines(data) +os.system('git commit data CMakeLists.txt -m "Release '+version+': Bump version"') + +print('\033[1;34m[*] Tag main...\033[0m') +os.system('git tag colobot-gold-'+version) + +print('\033[1;34m[*] Update dev on main...\033[0m') +os.system('git checkout dev') +os.system('git merge master --ff') +for i in range(len(data)): + m = re.match(r'^(#?)set\(COLOBOT_VERSION_(UNRELEASED|RELEASE_CODENAME)(.*)\)$', data[i]) + if m: + comment = (m.group(2) == 'RELEASE_CODENAME') + data[i] = ('#' if comment else '')+'set(COLOBOT_VERSION_'+m.group(2)+m.group(3)+')\n' +open('CMakeLists.txt', 'w').writelines(data) +os.system('git commit CMakeLists.txt -m "Post-release '+version+'"') +os.system('git checkout master') + +print('\033[1;32m[+] Done! Push when ready\033[0m') From 70853f2b8609eda2fa759b0a02a6654218f6ecc0 Mon Sep 17 00:00:00 2001 From: krzys-h Date: Wed, 2 Nov 2016 21:38:47 +0100 Subject: [PATCH 55/56] Add temporary hack for issue #828 --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 29865105..4f92d1c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,6 +133,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") message(STATUS "Detected GCC version 4.7+") set(NORMAL_CXX_FLAGS "-std=gnu++11 -Wall -Wold-style-cast -pedantic-errors") + set(NORMAL_CXX_FLAGS "${NORMAL_CXX_FLAGS} -fno-delete-null-pointer-checks") # Temporary hack, see #828 set(RELEASE_CXX_FLAGS "-O2") set(DEBUG_CXX_FLAGS "-g -O0") set(TEST_CXX_FLAGS "-pthread") From d43562883e802aa9cf0d39bdd0d927e1046c2b5d Mon Sep 17 00:00:00 2001 From: krzys-h Date: Wed, 2 Nov 2016 21:40:52 +0100 Subject: [PATCH 56/56] Remove unused imports from release.py --- tools/release.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tools/release.py b/tools/release.py index 526e41a4..81392726 100644 --- a/tools/release.py +++ b/tools/release.py @@ -1,8 +1,3 @@ -from __future__ import print_function -import fileinput -import re -import os - # Script to use when releasing new versions # Run from main repo directory with data submodule pulled in data/ # @@ -21,6 +16,9 @@ import os # # After finished, verify everything is correct and push the changes +import os +import re + print('\033[1;34m[*] Make sure all remote changes are pulled...\033[0m') os.system('git checkout dev') os.system('git pull --ff')