1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-03-29 21:56:54 +02:00

Merge branch 'develop' into handlersAbstraction

# Conflicts:
#	CI/linux/before_install.sh
#	CI/mac/before_install.sh
#	CI/mxe/before_install.sh
#	lib/CModHandler.cpp
#	lib/mapObjects/CObjectClassesHandler.cpp
#	lib/mapObjects/CObjectClassesHandler.h
#	lib/mapObjects/CommonConstructors.cpp
#	server/CGameHandler.cpp
#	test/CMakeLists.txt
#	test/spells/effects/TeleportTest.cpp
This commit is contained in:
Andrii Danylchenko 2021-07-16 00:32:13 +03:00
commit 9c8d776398
30 changed files with 1452 additions and 280 deletions

118
.github/workflows/github.yml vendored Normal file
View File

@ -0,0 +1,118 @@
name: CMake
on: [ push ]
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release
jobs:
build:
strategy:
matrix:
include:
- platform: linux
os: ubuntu-20.04
cc: clang-10
cxx: clang++-10
test: 0
- platform: linux
os: ubuntu-20.04
cc: gcc-9
cxx: g++-9
test: 0
- platform: mac
os: macos-latest
test: 0
pack: 1
extension: dmg
- platform: mxe
os: ubuntu-20.04
mxe: i686-w64-mingw32.shared
test: 0
pack: 1
extension: exe
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Dependencies
run: source ${{github.workspace}}/CI/${{matrix.platform}}/before_install.sh
env:
MXE_TARGET: ${{ matrix.mxe }}
- name: Git branch name
id: git-branch-name
uses: EthanSK/git-branch-name-action@v1
- name: Build Number
run: |
source ${{github.workspace}}/CI/get_package_name.sh
echo VCMI_PACKAGE_FILE_NAME="$VCMI_PACKAGE_FILE_NAME" >> $GITHUB_ENV
echo VCMI_PACKAGE_NAME_SUFFIX="$VCMI_PACKAGE_NAME_SUFFIX" >> $GITHUB_ENV
env:
PULL_REQUEST: ${{ github.event.pull_request.number }}
- name: Configure CMake
run: |
mkdir ${{github.workspace}}/build
cd ${{github.workspace}}/build
cmake -G Ninja .. -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \
-DENABLE_TEST=${{matrix.test}} \
-DPACKAGE_NAME_SUFFIX:STRING="$VCMI_PACKAGE_NAME_SUFFIX" \
-DPACKAGE_FILE_NAME:STRING="$VCMI_PACKAGE_FILE_NAME"
env:
CC: ${{ matrix.cc }}
CXX: ${{ matrix.cxx }}
- name: Build
run: |
cd ${{github.workspace}}/build
ninja
- name: Test
if: ${{ matrix.test == 1 }}
run: |
cd ${{github.workspace}}/build
ctest -C Release -V
- name: Pack
id: cpack
if: ${{ matrix.pack == 1 }}
run: |
cd ${{github.workspace}}/build
cpack
- name: Additional logs
if: ${{ failure() && steps.cpack.outcome == 'failure' && matrix.platform == 'mxe' }}
run: |
cat ${{github.workspace}}/build/_CPack_Packages/win32/NSIS/project.nsi
cat ${{github.workspace}}/build/_CPack_Packages/win32/NSIS/NSISOutput.log
- name: Artifacts
if: ${{ matrix.pack == 1 }}
uses: actions/upload-artifact@v2
with:
name: ${{ env.VCMI_PACKAGE_FILE_NAME }} - ${{ matrix.platform }}
path: ${{github.workspace}}/build/${{ env.VCMI_PACKAGE_FILE_NAME }}.${{ matrix.extension }}
- name: Upload build
if: ${{ matrix.pack == 1 && github.ref == 'refs/heads/develop' }}
run: |
cd ${{github.workspace}}/build
source ${{github.workspace}}/CI/upload_package.sh
env:
DEPLOY_RSA: ${{ secrets.DEPLOY_RSA }}
PACKAGE_EXTENSION: ${{ matrix.extension }}
- uses: act10ns/slack@v1
with:
status: ${{ job.status }}
channel: '#notifications'
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
if: always()

View File

@ -1166,12 +1166,12 @@ void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * ot
for(auto location : allArtifacts) for(auto location : allArtifacts)
{ {
if(location.slot == ArtifactPosition::MACH4 || location.slot == ArtifactPosition::SPELLBOOK)
continue; // don't attempt to move catapult and spellbook
if(location.relatedObj() == target && location.slot < ArtifactPosition::AFTER_LAST) if(location.relatedObj() == target && location.slot < ArtifactPosition::AFTER_LAST)
continue; //don't reequip artifact we already wear continue; //don't reequip artifact we already wear
if(location.slot == ArtifactPosition::MACH4) // don't attempt to move catapult
continue;
auto s = location.getSlot(); auto s = location.getSlot();
if(!s || s->locked) //we can't move locks if(!s || s->locked) //we can't move locks
continue; continue;

973
CI/NSIS.template.in Normal file
View File

@ -0,0 +1,973 @@
; CPack install script designed for a nmake build
;--------------------------------
; You must define these values
!define VERSION "@CPACK_PACKAGE_VERSION@"
!define PATCH "@CPACK_PACKAGE_VERSION_PATCH@"
!define INST_DIR "@CPACK_TEMPORARY_DIRECTORY@"
;--------------------------------
;Variables
Var MUI_TEMP
Var STARTMENU_FOLDER
Var SV_ALLUSERS
Var START_MENU
Var DO_NOT_ADD_TO_PATH
Var ADD_TO_PATH_ALL_USERS
Var ADD_TO_PATH_CURRENT_USER
Var INSTALL_DESKTOP
Var IS_DEFAULT_INSTALLDIR
;--------------------------------
;Include Modern UI
Unicode True
!include "MUI.nsh"
;Default installation folder
InstallDir "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@"
;--------------------------------
;General
;Name and file
Name "@CPACK_NSIS_PACKAGE_NAME@"
OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@"
;Set compression
SetCompressor @CPACK_NSIS_COMPRESSOR@
;Require administrator access
RequestExecutionLevel admin
@CPACK_NSIS_DEFINES@
!include Sections.nsh
;--- Component support macros: ---
; The code for the add/remove functionality is from:
; http://nsis.sourceforge.net/Add/Remove_Functionality
; It has been modified slightly and extended to provide
; inter-component dependencies.
Var AR_SecFlags
Var AR_RegFlags
@CPACK_NSIS_SECTION_SELECTED_VARS@
; Loads the "selected" flag for the section named SecName into the
; variable VarName.
!macro LoadSectionSelectedIntoVar SecName VarName
SectionGetFlags ${${SecName}} $${VarName}
IntOp $${VarName} $${VarName} & ${SF_SELECTED} ;Turn off all other bits
!macroend
; Loads the value of a variable... can we get around this?
!macro LoadVar VarName
IntOp $R0 0 + $${VarName}
!macroend
; Sets the value of a variable
!macro StoreVar VarName IntValue
IntOp $${VarName} 0 + ${IntValue}
!macroend
!macro InitSection SecName
; This macro reads component installed flag from the registry and
;changes checked state of the section on the components page.
;Input: section index constant name specified in Section command.
ClearErrors
;Reading component status from registry
ReadRegDWORD $AR_RegFlags HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@\Components\${SecName}" "Installed"
IfErrors "default_${SecName}"
;Status will stay default if registry value not found
;(component was never installed)
IntOp $AR_RegFlags $AR_RegFlags & ${SF_SELECTED} ;Turn off all other bits
SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading default section flags
IntOp $AR_SecFlags $AR_SecFlags & 0xFFFE ;Turn lowest (enabled) bit off
IntOp $AR_SecFlags $AR_RegFlags | $AR_SecFlags ;Change lowest bit
; Note whether this component was installed before
!insertmacro StoreVar ${SecName}_was_installed $AR_RegFlags
IntOp $R0 $AR_RegFlags & $AR_RegFlags
;Writing modified flags
SectionSetFlags ${${SecName}} $AR_SecFlags
"default_${SecName}:"
!insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected
!macroend
!macro FinishSection SecName
; This macro reads section flag set by user and removes the section
;if it is not selected.
;Then it writes component installed flag to registry
;Input: section index constant name specified in Section command.
SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading section flags
;Checking lowest bit:
IntOp $AR_SecFlags $AR_SecFlags & ${SF_SELECTED}
IntCmp $AR_SecFlags 1 "leave_${SecName}"
;Section is not selected:
;Calling Section uninstall macro and writing zero installed flag
!insertmacro "Remove_${${SecName}}"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@\Components\${SecName}" \
"Installed" 0
Goto "exit_${SecName}"
"leave_${SecName}:"
;Section is selected:
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@\Components\${SecName}" \
"Installed" 1
"exit_${SecName}:"
!macroend
!macro RemoveSection_CPack SecName
; This macro is used to call section's Remove_... macro
;from the uninstaller.
;Input: section index constant name specified in Section command.
!insertmacro "Remove_${${SecName}}"
!macroend
; Determine whether the selection of SecName changed
!macro MaybeSelectionChanged SecName
!insertmacro LoadVar ${SecName}_selected
SectionGetFlags ${${SecName}} $R1
IntOp $R1 $R1 & ${SF_SELECTED} ;Turn off all other bits
; See if the status has changed:
IntCmp $R0 $R1 "${SecName}_unchanged"
!insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected
IntCmp $R1 ${SF_SELECTED} "${SecName}_was_selected"
!insertmacro "Deselect_required_by_${SecName}"
goto "${SecName}_unchanged"
"${SecName}_was_selected:"
!insertmacro "Select_${SecName}_depends"
"${SecName}_unchanged:"
!macroend
;--- End of Add/Remove macros ---
;--------------------------------
;Interface Settings
!define MUI_HEADERIMAGE
!define MUI_ABORTWARNING
;----------------------------------------
; based upon a script of "Written by KiCHiK 2003-01-18 05:57:02"
;----------------------------------------
!verbose 3
!include "WinMessages.NSH"
!verbose 4
;====================================================
; get_NT_environment
; Returns: the selected environment
; Output : head of the stack
;====================================================
!macro select_NT_profile UN
Function ${UN}select_NT_profile
StrCmp $ADD_TO_PATH_ALL_USERS "1" 0 environment_single
DetailPrint "Selected environment for all users"
Push "all"
Return
environment_single:
DetailPrint "Selected environment for current user only."
Push "current"
Return
FunctionEnd
!macroend
!insertmacro select_NT_profile ""
!insertmacro select_NT_profile "un."
;----------------------------------------------------
!define NT_current_env 'HKCU "Environment"'
!define NT_all_env 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
!ifndef WriteEnvStr_RegKey
!ifdef ALL_USERS
!define WriteEnvStr_RegKey \
'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
!else
!define WriteEnvStr_RegKey 'HKCU "Environment"'
!endif
!endif
; AddToPath - Adds the given dir to the search path.
; Input - head of the stack
; Note - Win9x systems requires reboot
Function AddToPath
Exch $0
Push $1
Push $2
Push $3
# don't add if the path doesn't exist
IfFileExists "$0\*.*" "" AddToPath_done
ReadEnvStr $1 PATH
; if the path is too long for a NSIS variable NSIS will return a 0
; length string. If we find that, then warn and skip any path
; modification as it will trash the existing path.
StrLen $2 $1
IntCmp $2 0 CheckPathLength_ShowPathWarning CheckPathLength_Done CheckPathLength_Done
CheckPathLength_ShowPathWarning:
Messagebox MB_OK|MB_ICONEXCLAMATION "Warning! PATH too long installer unable to modify PATH!"
Goto AddToPath_done
CheckPathLength_Done:
Push "$1;"
Push "$0;"
Call StrStr
Pop $2
StrCmp $2 "" "" AddToPath_done
Push "$1;"
Push "$0\;"
Call StrStr
Pop $2
StrCmp $2 "" "" AddToPath_done
GetFullPathName /SHORT $3 $0
Push "$1;"
Push "$3;"
Call StrStr
Pop $2
StrCmp $2 "" "" AddToPath_done
Push "$1;"
Push "$3\;"
Call StrStr
Pop $2
StrCmp $2 "" "" AddToPath_done
Call IsNT
Pop $1
StrCmp $1 1 AddToPath_NT
; Not on NT
StrCpy $1 $WINDIR 2
FileOpen $1 "$1\autoexec.bat" a
FileSeek $1 -1 END
FileReadByte $1 $2
IntCmp $2 26 0 +2 +2 # DOS EOF
FileSeek $1 -1 END # write over EOF
FileWrite $1 "$\r$\nSET PATH=%PATH%;$3$\r$\n"
FileClose $1
SetRebootFlag true
Goto AddToPath_done
AddToPath_NT:
StrCmp $ADD_TO_PATH_ALL_USERS "1" ReadAllKey
ReadRegStr $1 ${NT_current_env} "PATH"
Goto DoTrim
ReadAllKey:
ReadRegStr $1 ${NT_all_env} "PATH"
DoTrim:
StrCmp $1 "" AddToPath_NTdoIt
Push $1
Call Trim
Pop $1
StrCpy $0 "$1;$0"
AddToPath_NTdoIt:
StrCmp $ADD_TO_PATH_ALL_USERS "1" WriteAllKey
WriteRegExpandStr ${NT_current_env} "PATH" $0
Goto DoSend
WriteAllKey:
WriteRegExpandStr ${NT_all_env} "PATH" $0
DoSend:
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
AddToPath_done:
Pop $3
Pop $2
Pop $1
Pop $0
FunctionEnd
; RemoveFromPath - Remove a given dir from the path
; Input: head of the stack
Function un.RemoveFromPath
Exch $0
Push $1
Push $2
Push $3
Push $4
Push $5
Push $6
IntFmt $6 "%c" 26 # DOS EOF
Call un.IsNT
Pop $1
StrCmp $1 1 unRemoveFromPath_NT
; Not on NT
StrCpy $1 $WINDIR 2
FileOpen $1 "$1\autoexec.bat" r
GetTempFileName $4
FileOpen $2 $4 w
GetFullPathName /SHORT $0 $0
StrCpy $0 "SET PATH=%PATH%;$0"
Goto unRemoveFromPath_dosLoop
unRemoveFromPath_dosLoop:
FileRead $1 $3
StrCpy $5 $3 1 -1 # read last char
StrCmp $5 $6 0 +2 # if DOS EOF
StrCpy $3 $3 -1 # remove DOS EOF so we can compare
StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoopRemoveLine
StrCmp $3 "$0$\n" unRemoveFromPath_dosLoopRemoveLine
StrCmp $3 "$0" unRemoveFromPath_dosLoopRemoveLine
StrCmp $3 "" unRemoveFromPath_dosLoopEnd
FileWrite $2 $3
Goto unRemoveFromPath_dosLoop
unRemoveFromPath_dosLoopRemoveLine:
SetRebootFlag true
Goto unRemoveFromPath_dosLoop
unRemoveFromPath_dosLoopEnd:
FileClose $2
FileClose $1
StrCpy $1 $WINDIR 2
Delete "$1\autoexec.bat"
CopyFiles /SILENT $4 "$1\autoexec.bat"
Delete $4
Goto unRemoveFromPath_done
unRemoveFromPath_NT:
StrCmp $ADD_TO_PATH_ALL_USERS "1" unReadAllKey
ReadRegStr $1 ${NT_current_env} "PATH"
Goto unDoTrim
unReadAllKey:
ReadRegStr $1 ${NT_all_env} "PATH"
unDoTrim:
StrCpy $5 $1 1 -1 # copy last char
StrCmp $5 ";" +2 # if last char != ;
StrCpy $1 "$1;" # append ;
Push $1
Push "$0;"
Call un.StrStr ; Find `$0;` in $1
Pop $2 ; pos of our dir
StrCmp $2 "" unRemoveFromPath_done
; else, it is in path
# $0 - path to add
# $1 - path var
StrLen $3 "$0;"
StrLen $4 $2
StrCpy $5 $1 -$4 # $5 is now the part before the path to remove
StrCpy $6 $2 "" $3 # $6 is now the part after the path to remove
StrCpy $3 $5$6
StrCpy $5 $3 1 -1 # copy last char
StrCmp $5 ";" 0 +2 # if last char == ;
StrCpy $3 $3 -1 # remove last char
StrCmp $ADD_TO_PATH_ALL_USERS "1" unWriteAllKey
WriteRegExpandStr ${NT_current_env} "PATH" $3
Goto unDoSend
unWriteAllKey:
WriteRegExpandStr ${NT_all_env} "PATH" $3
unDoSend:
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
unRemoveFromPath_done:
Pop $6
Pop $5
Pop $4
Pop $3
Pop $2
Pop $1
Pop $0
FunctionEnd
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Uninstall sutff
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
###########################################
# Utility Functions #
###########################################
;====================================================
; IsNT - Returns 1 if the current system is NT, 0
; otherwise.
; Output: head of the stack
;====================================================
; IsNT
; no input
; output, top of the stack = 1 if NT or 0 if not
;
; Usage:
; Call IsNT
; Pop $R0
; ($R0 at this point is 1 or 0)
!macro IsNT un
Function ${un}IsNT
Push $0
ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion
StrCmp $0 "" 0 IsNT_yes
; we are not NT.
Pop $0
Push 0
Return
IsNT_yes:
; NT!!!
Pop $0
Push 1
FunctionEnd
!macroend
!insertmacro IsNT ""
!insertmacro IsNT "un."
; StrStr
; input, top of stack = string to search for
; top of stack-1 = string to search in
; output, top of stack (replaces with the portion of the string remaining)
; modifies no other variables.
;
; Usage:
; Push "this is a long ass string"
; Push "ass"
; Call StrStr
; Pop $R0
; ($R0 at this point is "ass string")
!macro StrStr un
Function ${un}StrStr
Exch $R1 ; st=haystack,old$R1, $R1=needle
Exch ; st=old$R1,haystack
Exch $R2 ; st=old$R1,old$R2, $R2=haystack
Push $R3
Push $R4
Push $R5
StrLen $R3 $R1
StrCpy $R4 0
; $R1=needle
; $R2=haystack
; $R3=len(needle)
; $R4=cnt
; $R5=tmp
loop:
StrCpy $R5 $R2 $R3 $R4
StrCmp $R5 $R1 done
StrCmp $R5 "" done
IntOp $R4 $R4 + 1
Goto loop
done:
StrCpy $R1 $R2 "" $R4
Pop $R5
Pop $R4
Pop $R3
Pop $R2
Exch $R1
FunctionEnd
!macroend
!insertmacro StrStr ""
!insertmacro StrStr "un."
Function Trim ; Added by Pelaca
Exch $R1
Push $R2
Loop:
StrCpy $R2 "$R1" 1 -1
StrCmp "$R2" " " RTrim
StrCmp "$R2" "$\n" RTrim
StrCmp "$R2" "$\r" RTrim
StrCmp "$R2" ";" RTrim
GoTo Done
RTrim:
StrCpy $R1 "$R1" -1
Goto Loop
Done:
Pop $R2
Exch $R1
FunctionEnd
Function ConditionalAddToRegisty
Pop $0
Pop $1
StrCmp "$0" "" ConditionalAddToRegisty_EmptyString
WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" \
"$1" "$0"
;MessageBox MB_OK "Set Registry: '$1' to '$0'"
DetailPrint "Set install registry entry: '$1' to '$0'"
ConditionalAddToRegisty_EmptyString:
FunctionEnd
;--------------------------------
!ifdef CPACK_USES_DOWNLOAD
Function DownloadFile
IfFileExists $INSTDIR\* +2
CreateDirectory $INSTDIR
Pop $0
; Skip if already downloaded
IfFileExists $INSTDIR\$0 0 +2
Return
StrCpy $1 "@CPACK_DOWNLOAD_SITE@"
try_again:
NSISdl::download "$1/$0" "$INSTDIR\$0"
Pop $1
StrCmp $1 "success" success
StrCmp $1 "Cancelled" cancel
MessageBox MB_OK "Download failed: $1"
cancel:
Return
success:
FunctionEnd
!endif
;--------------------------------
; Installation types
@CPACK_NSIS_INSTALLATION_TYPES@
;--------------------------------
; Component sections
@CPACK_NSIS_COMPONENT_SECTIONS@
;--------------------------------
; Define some macro setting for the gui
@CPACK_NSIS_INSTALLER_MUI_ICON_CODE@
@CPACK_NSIS_INSTALLER_ICON_CODE@
@CPACK_NSIS_INSTALLER_MUI_WELCOMEFINISH_CODE@
@CPACK_NSIS_INSTALLER_MUI_UNWELCOMEFINISH_CODE@
@CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC@
@CPACK_NSIS_INSTALLER_MUI_FINISHPAGE_RUN_CODE@
;--------------------------------
;Pages
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@"
Page custom InstallOptionsPage
!insertmacro MUI_PAGE_DIRECTORY
;Start Menu Folder Page Configuration
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "SHCTX"
!define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder"
!insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER
@CPACK_NSIS_PAGE_COMPONENTS@
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
;--------------------------------
;Languages
!insertmacro MUI_LANGUAGE "English" ;first language is the default language
!insertmacro MUI_LANGUAGE "Albanian"
!insertmacro MUI_LANGUAGE "Arabic"
!insertmacro MUI_LANGUAGE "Basque"
!insertmacro MUI_LANGUAGE "Belarusian"
!insertmacro MUI_LANGUAGE "Bosnian"
!insertmacro MUI_LANGUAGE "Breton"
!insertmacro MUI_LANGUAGE "Bulgarian"
!insertmacro MUI_LANGUAGE "Croatian"
!insertmacro MUI_LANGUAGE "Czech"
!insertmacro MUI_LANGUAGE "Danish"
!insertmacro MUI_LANGUAGE "Dutch"
!insertmacro MUI_LANGUAGE "Estonian"
!insertmacro MUI_LANGUAGE "Farsi"
!insertmacro MUI_LANGUAGE "Finnish"
!insertmacro MUI_LANGUAGE "French"
!insertmacro MUI_LANGUAGE "German"
!insertmacro MUI_LANGUAGE "Greek"
!insertmacro MUI_LANGUAGE "Hebrew"
!insertmacro MUI_LANGUAGE "Hungarian"
!insertmacro MUI_LANGUAGE "Icelandic"
!insertmacro MUI_LANGUAGE "Indonesian"
!insertmacro MUI_LANGUAGE "Irish"
!insertmacro MUI_LANGUAGE "Italian"
!insertmacro MUI_LANGUAGE "Japanese"
!insertmacro MUI_LANGUAGE "Korean"
!insertmacro MUI_LANGUAGE "Kurdish"
!insertmacro MUI_LANGUAGE "Latvian"
!insertmacro MUI_LANGUAGE "Lithuanian"
!insertmacro MUI_LANGUAGE "Luxembourgish"
!insertmacro MUI_LANGUAGE "Macedonian"
!insertmacro MUI_LANGUAGE "Malay"
!insertmacro MUI_LANGUAGE "Mongolian"
!insertmacro MUI_LANGUAGE "Norwegian"
!insertmacro MUI_LANGUAGE "Polish"
!insertmacro MUI_LANGUAGE "Portuguese"
!insertmacro MUI_LANGUAGE "PortugueseBR"
!insertmacro MUI_LANGUAGE "Romanian"
!insertmacro MUI_LANGUAGE "Russian"
!insertmacro MUI_LANGUAGE "Serbian"
!insertmacro MUI_LANGUAGE "SerbianLatin"
!insertmacro MUI_LANGUAGE "SimpChinese"
!insertmacro MUI_LANGUAGE "Slovak"
!insertmacro MUI_LANGUAGE "Slovenian"
!insertmacro MUI_LANGUAGE "Spanish"
!insertmacro MUI_LANGUAGE "Swedish"
!insertmacro MUI_LANGUAGE "Thai"
!insertmacro MUI_LANGUAGE "TradChinese"
!insertmacro MUI_LANGUAGE "Turkish"
!insertmacro MUI_LANGUAGE "Ukrainian"
!insertmacro MUI_LANGUAGE "Welsh"
;--------------------------------
;Reserve Files
;These files should be inserted before other files in the data block
;Keep these lines before any File command
;Only for solid compression (by default, solid compression is enabled for BZIP2 and LZMA)
ReserveFile "NSIS.InstallOptions.ini"
!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS
;--------------------------------
;Installer Sections
Section "-Core installation"
;Use the entire tree produced by the INSTALL target. Keep the
;list of directories here in sync with the RMDir commands below.
SetOutPath "$INSTDIR"
@CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS@
@CPACK_NSIS_FULL_INSTALL@
;Store installation folder
WriteRegStr SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR
;Create uninstaller
WriteUninstaller "$INSTDIR\Uninstall.exe"
Push "DisplayName"
Push "@CPACK_NSIS_DISPLAY_NAME@"
Call ConditionalAddToRegisty
Push "DisplayVersion"
Push "@CPACK_PACKAGE_VERSION@"
Call ConditionalAddToRegisty
Push "Publisher"
Push "@CPACK_PACKAGE_VENDOR@"
Call ConditionalAddToRegisty
Push "UninstallString"
Push "$INSTDIR\Uninstall.exe"
Call ConditionalAddToRegisty
Push "NoRepair"
Push "1"
Call ConditionalAddToRegisty
!ifdef CPACK_NSIS_ADD_REMOVE
;Create add/remove functionality
Push "ModifyPath"
Push "$INSTDIR\AddRemove.exe"
Call ConditionalAddToRegisty
!else
Push "NoModify"
Push "1"
Call ConditionalAddToRegisty
!endif
; Optional registration
Push "DisplayIcon"
Push "$INSTDIR\@CPACK_NSIS_INSTALLED_ICON_NAME@"
Call ConditionalAddToRegisty
Push "HelpLink"
Push "@CPACK_NSIS_HELP_LINK@"
Call ConditionalAddToRegisty
Push "URLInfoAbout"
Push "@CPACK_NSIS_URL_INFO_ABOUT@"
Call ConditionalAddToRegisty
Push "Contact"
Push "@CPACK_NSIS_CONTACT@"
Call ConditionalAddToRegisty
!insertmacro MUI_INSTALLOPTIONS_READ $INSTALL_DESKTOP "NSIS.InstallOptions.ini" "Field 5" "State"
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
;Create shortcuts
CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER"
@CPACK_NSIS_CREATE_ICONS@
@CPACK_NSIS_CREATE_ICONS_EXTRA@
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
;Read a value from an InstallOptions INI file
!insertmacro MUI_INSTALLOPTIONS_READ $DO_NOT_ADD_TO_PATH "NSIS.InstallOptions.ini" "Field 2" "State"
!insertmacro MUI_INSTALLOPTIONS_READ $ADD_TO_PATH_ALL_USERS "NSIS.InstallOptions.ini" "Field 3" "State"
!insertmacro MUI_INSTALLOPTIONS_READ $ADD_TO_PATH_CURRENT_USER "NSIS.InstallOptions.ini" "Field 4" "State"
; Write special uninstall registry entries
Push "StartMenu"
Push "$STARTMENU_FOLDER"
Call ConditionalAddToRegisty
Push "DoNotAddToPath"
Push "$DO_NOT_ADD_TO_PATH"
Call ConditionalAddToRegisty
Push "AddToPathAllUsers"
Push "$ADD_TO_PATH_ALL_USERS"
Call ConditionalAddToRegisty
Push "AddToPathCurrentUser"
Push "$ADD_TO_PATH_CURRENT_USER"
Call ConditionalAddToRegisty
Push "InstallToDesktop"
Push "$INSTALL_DESKTOP"
Call ConditionalAddToRegisty
!insertmacro MUI_STARTMENU_WRITE_END
@CPACK_NSIS_EXTRA_INSTALL_COMMANDS@
SectionEnd
Section "-Add to path"
Push $INSTDIR\bin
StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 doNotAddToPath
StrCmp $DO_NOT_ADD_TO_PATH "1" doNotAddToPath 0
Call AddToPath
doNotAddToPath:
SectionEnd
;--------------------------------
; Create custom pages
Function InstallOptionsPage
!insertmacro MUI_HEADER_TEXT "Install Options" "Choose options for installing @CPACK_NSIS_PACKAGE_NAME@"
!insertmacro MUI_INSTALLOPTIONS_DISPLAY "NSIS.InstallOptions.ini"
FunctionEnd
;--------------------------------
; determine admin versus local install
Function un.onInit
ClearErrors
UserInfo::GetName
IfErrors noLM
Pop $0
UserInfo::GetAccountType
Pop $1
StrCmp $1 "Admin" 0 +3
SetShellVarContext all
;MessageBox MB_OK 'User "$0" is in the Admin group'
Goto done
StrCmp $1 "Power" 0 +3
SetShellVarContext all
;MessageBox MB_OK 'User "$0" is in the Power Users group'
Goto done
noLM:
;Get installation folder from registry if available
done:
FunctionEnd
;--- Add/Remove callback functions: ---
!macro SectionList MacroName
;This macro used to perform operation on multiple sections.
;List all of your components in following manner here.
@CPACK_NSIS_COMPONENT_SECTION_LIST@
!macroend
Section -FinishComponents
;Removes unselected components and writes component status to registry
!insertmacro SectionList "FinishSection"
!ifdef CPACK_NSIS_ADD_REMOVE
; Get the name of the installer executable
System::Call 'kernel32::GetModuleFileNameA(i 0, t .R0, i 1024) i r1'
StrCpy $R3 $R0
; Strip off the last 13 characters, to see if we have AddRemove.exe
StrLen $R1 $R0
IntOp $R1 $R0 - 13
StrCpy $R2 $R0 13 $R1
StrCmp $R2 "AddRemove.exe" addremove_installed
; We're not running AddRemove.exe, so install it
CopyFiles $R3 $INSTDIR\AddRemove.exe
addremove_installed:
!endif
SectionEnd
;--- End of Add/Remove callback functions ---
;--------------------------------
; Component dependencies
Function .onSelChange
!insertmacro SectionList MaybeSelectionChanged
FunctionEnd
;--------------------------------
;Uninstaller Section
Section "Uninstall"
ReadRegStr $START_MENU SHCTX \
"Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "StartMenu"
;MessageBox MB_OK "Start menu is in: $START_MENU"
ReadRegStr $DO_NOT_ADD_TO_PATH SHCTX \
"Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "DoNotAddToPath"
ReadRegStr $ADD_TO_PATH_ALL_USERS SHCTX \
"Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "AddToPathAllUsers"
ReadRegStr $ADD_TO_PATH_CURRENT_USER SHCTX \
"Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "AddToPathCurrentUser"
;MessageBox MB_OK "Add to path: $DO_NOT_ADD_TO_PATH all users: $ADD_TO_PATH_ALL_USERS"
ReadRegStr $INSTALL_DESKTOP SHCTX \
"Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "InstallToDesktop"
;MessageBox MB_OK "Install to desktop: $INSTALL_DESKTOP "
@CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS@
;Remove files we installed.
;Keep the list of directories here in sync with the File commands above.
@CPACK_NSIS_DELETE_FILES@
@CPACK_NSIS_DELETE_DIRECTORIES@
!ifdef CPACK_NSIS_ADD_REMOVE
;Remove the add/remove program
Delete "$INSTDIR\AddRemove.exe"
!endif
;Remove the uninstaller itself.
Delete "$INSTDIR\Uninstall.exe"
DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
;Remove the installation directory if it is empty.
RMDir "$INSTDIR"
; Remove the registry entries.
DeleteRegKey SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
; Removes all optional components
!insertmacro SectionList "RemoveSection_CPack"
!insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP
Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk"
@CPACK_NSIS_DELETE_ICONS@
@CPACK_NSIS_DELETE_ICONS_EXTRA@
;Delete empty start menu parent diretories
StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP"
startMenuDeleteLoop:
ClearErrors
RMDir $MUI_TEMP
GetFullPathName $MUI_TEMP "$MUI_TEMP\.."
IfErrors startMenuDeleteLoopDone
StrCmp "$MUI_TEMP" "$SMPROGRAMS" startMenuDeleteLoopDone startMenuDeleteLoop
startMenuDeleteLoopDone:
; If the user changed the shortcut, then untinstall may not work. This should
; try to fix it.
StrCpy $MUI_TEMP "$START_MENU"
Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk"
@CPACK_NSIS_DELETE_ICONS_EXTRA@
;Delete empty start menu parent diretories
StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP"
secondStartMenuDeleteLoop:
ClearErrors
RMDir $MUI_TEMP
GetFullPathName $MUI_TEMP "$MUI_TEMP\.."
IfErrors secondStartMenuDeleteLoopDone
StrCmp "$MUI_TEMP" "$SMPROGRAMS" secondStartMenuDeleteLoopDone secondStartMenuDeleteLoop
secondStartMenuDeleteLoopDone:
DeleteRegKey /ifempty SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
Push $INSTDIR\bin
StrCmp $DO_NOT_ADD_TO_PATH_ "1" doNotRemoveFromPath 0
Call un.RemoveFromPath
doNotRemoveFromPath:
SectionEnd
;--------------------------------
; determine admin versus local install
; Is install for "AllUsers" or "JustMe"?
; Default to "JustMe" - set to "AllUsers" if admin or on Win9x
; This function is used for the very first "custom page" of the installer.
; This custom page does not show up visibly, but it executes prior to the
; first visible page and sets up $INSTDIR properly...
; Choose different default installation folder based on SV_ALLUSERS...
; "Program Files" for AllUsers, "My Documents" for JustMe...
Function .onInit
StrCmp "@CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL@" "ON" 0 inst
ReadRegStr $0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "UninstallString"
StrCmp $0 "" inst
MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION \
"@CPACK_NSIS_PACKAGE_NAME@ is already installed. $\n$\nDo you want to uninstall the old version before installing the new one?" \
/SD IDYES IDYES uninst IDNO inst
Abort
;Run the uninstaller
uninst:
ClearErrors
StrLen $2 "\Uninstall.exe"
StrCpy $3 $0 -$2 # remove "\Uninstall.exe" from UninstallString to get path
ExecWait '"$0" /S _?=$3' ;Do not copy the uninstaller to a temp file
IfErrors uninst_failed inst
uninst_failed:
MessageBox MB_OK|MB_ICONSTOP "Uninstall failed."
Abort
inst:
; Reads components status for registry
!insertmacro SectionList "InitSection"
; check to see if /D has been used to change
; the install directory by comparing it to the
; install directory that is expected to be the
; default
StrCpy $IS_DEFAULT_INSTALLDIR 0
StrCmp "$INSTDIR" "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" 0 +2
StrCpy $IS_DEFAULT_INSTALLDIR 1
StrCpy $SV_ALLUSERS "JustMe"
; if default install dir then change the default
; if it is installed for JustMe
StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2
StrCpy $INSTDIR "$DOCUMENTS\@CPACK_PACKAGE_INSTALL_DIRECTORY@"
ClearErrors
UserInfo::GetName
IfErrors noLM
Pop $0
UserInfo::GetAccountType
Pop $1
StrCmp $1 "Admin" 0 +4
SetShellVarContext all
;MessageBox MB_OK 'User "$0" is in the Admin group'
StrCpy $SV_ALLUSERS "AllUsers"
Goto done
StrCmp $1 "Power" 0 +4
SetShellVarContext all
;MessageBox MB_OK 'User "$0" is in the Power Users group'
StrCpy $SV_ALLUSERS "AllUsers"
Goto done
noLM:
StrCpy $SV_ALLUSERS "AllUsers"
;Get installation folder from registry if available
done:
StrCmp $SV_ALLUSERS "AllUsers" 0 +3
StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2
StrCpy $INSTDIR "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@"
StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 noOptionsPage
!insertmacro MUI_INSTALLOPTIONS_EXTRACT "NSIS.InstallOptions.ini"
noOptionsPage:
FunctionEnd

View File

@ -1,105 +1,105 @@
# Common configuration for all branches # Common configuration for all branches
version: 1.0.{build} version: 1.0.{build}
max_jobs: 2 max_jobs: 2
clone_depth: 10 clone_depth: 10
clone_folder: c:\projects\vcmi\source clone_folder: c:\projects\vcmi\source
install: install:
- bash c:\projects\vcmi\source\CI\msvc\install.sh - bash c:\projects\vcmi\source\CI\msvc\install.sh
build_script: build_script:
- cmd: c:\projects\vcmi\source\CI\msvc\build_script.bat - cmd: c:\projects\vcmi\source\CI\msvc\build_script.bat
artifacts: artifacts:
- path: build_$(VCMI_BUILD_PLATFORM)\*.exe - path: build_$(VCMI_BUILD_PLATFORM)\*.exe
notifications: notifications:
- provider: Slack - provider: Slack
incoming_webhook: incoming_webhook:
secure: zxT3HTnxL744HiSv7ig7sjGL4LmJ8n3MsY8PEA/kinbVMkmcxrSgVBVkHV79RfSWSyq4oLMSRvMMpG8SuDWnf6oK/qvgaiAWfwwlCIiA7uQ= secure: zxT3HTnxL744HiSv7ig7sjGL4LmJ8n3MsY8PEA/kinbVMkmcxrSgVBVkHV79RfSWSyq4oLMSRvMMpG8SuDWnf6oK/qvgaiAWfwwlCIiA7uQ=
# Branch-specific configuration # Branch-specific configuration
for: for:
# Extended configuration for toolchain_test branch # Extended configuration for toolchain_test branch
- -
branches: branches:
only: only:
- toolchain_test - toolchain_test
environment: environment:
matrix: matrix:
- NAME: MSVS 2015 x86 - Release - NAME: MSVS 2015 x86 - Release
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
VCMI_GENERATOR: Visual Studio 14 2015 VCMI_GENERATOR: Visual Studio 14 2015
VCMI_BUILD_PLATFORM: x86 VCMI_BUILD_PLATFORM: x86
VCMI_BUILD_CONFIGURATION: Release VCMI_BUILD_CONFIGURATION: Release
- NAME: MSVS 2015 x86 - Debug - NAME: MSVS 2015 x86 - Debug
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
VCMI_GENERATOR: Visual Studio 14 2015 VCMI_GENERATOR: Visual Studio 14 2015
VCMI_BUILD_PLATFORM: x86 VCMI_BUILD_PLATFORM: x86
VCMI_BUILD_CONFIGURATION: Debug VCMI_BUILD_CONFIGURATION: Debug
- NAME: MSVS 2015 x64 - Release - NAME: MSVS 2015 x64 - Release
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
VCMI_GENERATOR: Visual Studio 14 2015 Win64 VCMI_GENERATOR: Visual Studio 14 2015 Win64
VCMI_BUILD_PLATFORM: x64 VCMI_BUILD_PLATFORM: x64
VCMI_BUILD_CONFIGURATION: Release VCMI_BUILD_CONFIGURATION: Release
- NAME: MSVS 2015 x64 - Debug - NAME: MSVS 2015 x64 - Debug
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
VCMI_GENERATOR: Visual Studio 14 2015 Win64 VCMI_GENERATOR: Visual Studio 14 2015 Win64
VCMI_BUILD_PLATFORM: x64 VCMI_BUILD_PLATFORM: x64
VCMI_BUILD_CONFIGURATION: Debug VCMI_BUILD_CONFIGURATION: Debug
- NAME: MSVS 2017 x86 - Release - NAME: MSVS 2017 x86 - Release
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
VCMI_GENERATOR: Visual Studio 15 2017 VCMI_GENERATOR: Visual Studio 15 2017
VCMI_BUILD_PLATFORM: x86 VCMI_BUILD_PLATFORM: x86
VCMI_BUILD_CONFIGURATION: Release VCMI_BUILD_CONFIGURATION: Release
- NAME: MSVS 2017 x86 - Debug - NAME: MSVS 2017 x86 - Debug
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
VCMI_GENERATOR: Visual Studio 15 2017 VCMI_GENERATOR: Visual Studio 15 2017
VCMI_BUILD_PLATFORM: x86 VCMI_BUILD_PLATFORM: x86
VCMI_BUILD_CONFIGURATION: Debug VCMI_BUILD_CONFIGURATION: Debug
- NAME: MSVS 2017 x64 - Release - NAME: MSVS 2017 x64 - Release
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
VCMI_GENERATOR: Visual Studio 15 2017 Win64 VCMI_GENERATOR: Visual Studio 15 2017 Win64
VCMI_BUILD_PLATFORM: x64 VCMI_BUILD_PLATFORM: x64
VCMI_BUILD_CONFIGURATION: Release VCMI_BUILD_CONFIGURATION: Release
- NAME: MSVS 2017 x64 - Debug - NAME: MSVS 2017 x64 - Debug
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
VCMI_GENERATOR: Visual Studio 15 2017 Win64 VCMI_GENERATOR: Visual Studio 15 2017 Win64
VCMI_BUILD_PLATFORM: x64 VCMI_BUILD_PLATFORM: x64
VCMI_BUILD_CONFIGURATION: Debug VCMI_BUILD_CONFIGURATION: Debug
# Special configuration for coverity_scan branch # Special configuration for coverity_scan branch
- -
branches: branches:
only: only:
- coverity_scan - coverity_scan
environment: environment:
matrix: matrix:
- NAME: Coverity - MSVS 2017 x86 - Release - NAME: Coverity - MSVS 2017 x86 - Release
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
VCMI_GENERATOR: Visual Studio 15 2017 VCMI_GENERATOR: Visual Studio 15 2017
VCMI_BUILD_PLATFORM: x86 VCMI_BUILD_PLATFORM: x86
VCMI_BUILD_CONFIGURATION: Release VCMI_BUILD_CONFIGURATION: Release
environment: environment:
coverity_token: coverity_token:
secure: XNnpYevnZxGmXW1zLu+3js2S+pqfWPQmL26hVgOTBTI= secure: XNnpYevnZxGmXW1zLu+3js2S+pqfWPQmL26hVgOTBTI=
coverity_email: coverity_email:
secure: JDd5yXvYaq/yJEVjoadEhA== secure: JDd5yXvYaq/yJEVjoadEhA==
build_script: build_script:
- cmd: c:\projects\vcmi\source\CI\msvc\coverity_build_script.bat - cmd: c:\projects\vcmi\source\CI\msvc\coverity_build_script.bat
after_test: after_test:
- ps: c:\projects\vcmi\source\CI\msvc\coverity_upload_script.ps - ps: c:\projects\vcmi\source\CI\msvc\coverity_upload_script.ps
# Default configuration for all other branches # Default configuration for all other branches
- -
environment: environment:
matrix: matrix:
- NAME: MSVS 2017 x86 - Release - NAME: MSVS 2017 x86 - Release
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
VCMI_GENERATOR: Visual Studio 15 2017 VCMI_GENERATOR: Visual Studio 15 2017
VCMI_BUILD_PLATFORM: x86 VCMI_BUILD_PLATFORM: x86
VCMI_BUILD_CONFIGURATION: Release VCMI_BUILD_CONFIGURATION: Release
- NAME: MSVS 2017 x64 - Release - NAME: MSVS 2017 x64 - Release
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
VCMI_GENERATOR: Visual Studio 15 2017 Win64 VCMI_GENERATOR: Visual Studio 15 2017 Win64
VCMI_BUILD_PLATFORM: x64 VCMI_BUILD_PLATFORM: x64
VCMI_BUILD_CONFIGURATION: Release VCMI_BUILD_CONFIGURATION: Release

View File

@ -13,6 +13,13 @@ then
TMP_BRANCH="$APPVEYOR_REPO_BRANCH" TMP_BRANCH="$APPVEYOR_REPO_BRANCH"
TMP_PRID="$APPVEYOR_PULL_REQUEST_NUMBER" TMP_PRID="$APPVEYOR_PULL_REQUEST_NUMBER"
TMP_COMMIT="$APPVEYOR_REPO_COMMIT" TMP_COMMIT="$APPVEYOR_REPO_COMMIT"
elif [ ! -z "${GITHUB_RUN_ID}" ];
then
echo "Using Github environment variables!"
TMP_JOBID="$GITHUB_RUN_ID"
TMP_BRANCH=${GITHUB_REF#refs/heads/}
TMP_PRID="$PULL_REQUEST"
TMP_COMMIT=$(git rev-parse --short "$GITHUB_SHA")
else else
echo "No Travir or AppVeyor environment variables found!" echo "No Travir or AppVeyor environment variables found!"
exit exit

View File

@ -1,27 +1,16 @@
#!/bin/sh #!/bin/sh
#new Clang sudo apt-get update
sudo add-apt-repository --yes ppa:h-rayflood/llvm
#new SDL2 # Boost
sudo add-apt-repository --yes ppa:zoogie/sdl2-snapshots wget -nv https://boostorg.jfrog.io/artifactory/main/release/1.66.0/source/boost_1_66_0.tar.gz
tar xfz boost_1_66_0.tar.gz
cd boost_1_66_0
./bootstrap.sh --with-libraries=program_options,filesystem,system,thread,locale,date_time
./b2
sudo ./b2 install
#new Qt # Dependencies
sudo add-apt-repository --yes ppa:beineri/opt-qt571-trusty sudo apt-get install libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev
sudo apt-get install qtbase5-dev
#new CMake sudo apt-get install ninja-build zlib1g-dev libavformat-dev libswscale-dev libtbb-dev libluajit-5.1-dev
sudo add-apt-repository --yes ppa:george-edison55/cmake-3.x
sudo apt-get update -qq
sudo apt-get install -qq $SUPPORT
sudo apt-get install -qq $PACKAGE
sudo apt-get install -qq cmake ninja-build libboost1.54-all-dev zlib1g-dev
sudo apt-get install -qq libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev
sudo apt-get install -qq libavformat-dev libswscale-dev
sudo apt-get install -qq qt57declarative
sudo apt-get install -qq libluajit-5.1-dev
#setup compiler
source /opt/qt57/bin/qt57-env.sh
export CC=${REAL_CC} CXX=${REAL_CXX}

View File

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
brew update brew update
brew install smpeg2 libpng freetype sdl2 sdl2_ttf sdl2_image qt5 ffmpeg ninja luajit brew install smpeg2 libpng freetype qt5 ffmpeg ninja boost tbb luajit
brew install sdl2_mixer brew install sdl2 sdl2_ttf sdl2_image sdl2_mixer
export CMAKE_PREFIX_PATH="/usr/local/opt/qt5:$CMAKE_PREFIX_PATH" echo CMAKE_PREFIX_PATH="/usr/local/opt/qt5:$CMAKE_PREFIX_PATH" >> $GITHUB_ENV

View File

@ -1,15 +0,0 @@
#!/bin/sh
if [ -z "$encrypted_1d30f79f8582_key" ];
then
# Due to security measures travis not expose encryption keys for PR from forks
exit 0
fi
cpack
touch /tmp/deploy_rsa
chmod 600 /tmp/deploy_rsa
openssl aes-256-cbc -K $encrypted_1d30f79f8582_key -iv $encrypted_1d30f79f8582_iv -in $TRAVIS_BUILD_DIR/CI/deploy_rsa.enc -out /tmp/deploy_rsa -d
eval "$(ssh-agent -s)"
ssh-add /tmp/deploy_rsa
sftp -r -o StrictHostKeyChecking=no travis@beholder.vcmi.eu <<< "put $VCMI_PACKAGE_FILE_NAME.dmg /incoming/$VCMI_PACKAGE_FILE_NAME.dmg"

View File

@ -4,7 +4,7 @@
sudo apt-get install -qq nsis ninja-build sudo apt-get install -qq nsis ninja-build
# MXE repository was too slow for Travis far too often # MXE repository was too slow for Travis far too often
wget https://github.com/vcmi/vcmi-deps-mxe/releases/download/2019-06-28/mxe-i686-w64-mingw32.shared-2019-06-28.tar wget -nv https://github.com/vcmi/vcmi-deps-mxe/releases/download/2019-06-28/mxe-i686-w64-mingw32.shared-2019-06-28.tar
tar -xvf mxe-i686-w64-mingw32.shared-2019-06-28.tar tar -xvf mxe-i686-w64-mingw32.shared-2019-06-28.tar
sudo dpkg -i mxe-*.deb sudo dpkg -i mxe-*.deb
sudo apt-get install -f --yes sudo apt-get install -f --yes
@ -36,5 +36,7 @@ mxe-i686-w64-mingw32.static-luajit
fi # Disable fi # Disable
# alias for CMake # alias for CMake
sudo mv /usr/bin/cmake /usr/bin/cmake.orig
sudo ln -s /usr/lib/mxe/usr/bin/$MXE_TARGET-cmake /usr/bin/cmake CMAKE_LOCATION=$(which cmake)
sudo mv $CMAKE_LOCATION $CMAKE_LOCATION.orig
sudo ln -s /usr/lib/mxe/usr/bin/$MXE_TARGET-cmake $CMAKE_LOCATION

View File

@ -1,16 +0,0 @@
#!/bin/sh
if [ -z "$encrypted_1d30f79f8582_key" ];
then
# Due to security measures travis not expose encryption keys for PR from forks
exit 0
fi
cpack
touch /tmp/deploy_rsa
chmod 600 /tmp/deploy_rsa
openssl aes-256-cbc -K $encrypted_1d30f79f8582_key -iv $encrypted_1d30f79f8582_iv -in $TRAVIS_BUILD_DIR/CI/deploy_rsa.enc -out /tmp/deploy_rsa -d
eval "$(ssh-agent -s)"
ssh-add /tmp/deploy_rsa
sftp -r -o StrictHostKeyChecking=no travis@beholder.vcmi.eu <<< "put $VCMI_PACKAGE_FILE_NAME.exe /incoming/$VCMI_PACKAGE_FILE_NAME.exe"

15
CI/upload_package.sh Normal file
View File

@ -0,0 +1,15 @@
#!/bin/sh
if [ -z "$DEPLOY_RSA" ];
then
# Due to security measures travis not expose encryption keys for PR from forks
echo "Build generation is skipped for forks"
exit 0
fi
echo "$DEPLOY_RSA" > /tmp/deploy_rsa
chmod 600 /tmp/deploy_rsa
eval "$(ssh-agent -s)"
ssh-add /tmp/deploy_rsa
sftp -r -o StrictHostKeyChecking=no travis@beholder.vcmi.eu <<< "put $VCMI_PACKAGE_FILE_NAME.$PACKAGE_EXTENSION /incoming/$VCMI_PACKAGE_FILE_NAME.$PACKAGE_EXTENSION"

View File

@ -64,7 +64,7 @@ set(PACKAGE_FILE_NAME "" CACHE STRING "Override for CPack package filename")
# Miscellaneous options # # Miscellaneous options #
############################################ ############################################
set(CMAKE_MODULE_PATH ${CMAKE_HOME_DIRECTORY}/cmake_modules) set(CMAKE_MODULE_PATH ${CMAKE_HOME_DIRECTORY}/cmake_modules ${PROJECT_SOURCE_DIR}/CI)
# Contains custom functions and macros, but don't altering any options # Contains custom functions and macros, but don't altering any options
include(VCMIUtils) include(VCMIUtils)
vcmi_print_important_variables() vcmi_print_important_variables()
@ -88,7 +88,7 @@ define_property(
# Generate Version.cpp # Generate Version.cpp
if(ENABLE_GITVERSION) if(ENABLE_GITVERSION)
add_custom_target(update_version ALL add_custom_target(update_version ALL
COMMAND ${CMAKE_COMMAND} -DGIT_SHA1="${GIT_SHA1}" -P "${CMAKE_MODULE_PATH}/Version.cmake" COMMAND ${CMAKE_COMMAND} -DGIT_SHA1="${GIT_SHA1}" -P "${PROJECT_SOURCE_DIR}/cmake_modules/Version.cmake"
) )
else() else()
add_definitions(-DVCMI_NO_EXTRA_VERSION) add_definitions(-DVCMI_NO_EXTRA_VERSION)

View File

@ -191,7 +191,7 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
#include <boost/format.hpp> #include <boost/format.hpp>
#include <boost/functional/hash.hpp> #include <boost/functional/hash.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#ifndef VCMI_ANDROID #ifdef VCMI_WINDOWS
#include <boost/locale/generator.hpp> #include <boost/locale/generator.hpp>
#endif #endif
#include <boost/logic/tribool.hpp> #include <boost/logic/tribool.hpp>

View File

@ -656,19 +656,20 @@ CFocusable::~CFocusable()
focusables -= this; focusables -= this;
} }
void CFocusable::giveFocus() void CFocusable::giveFocus()
{ {
focus = true;
focusGot();
redraw();
if(inputWithFocus) if(inputWithFocus)
{ {
inputWithFocus->focus = false; inputWithFocus->focus = false;
inputWithFocus->focusLost();
inputWithFocus->redraw(); inputWithFocus->redraw();
} }
focus = true;
inputWithFocus = this; inputWithFocus = this;
focusGot();
redraw();
} }
void CFocusable::moveFocus() void CFocusable::moveFocus()

View File

@ -1158,7 +1158,7 @@ CGameState::CrossoverHeroesList CGameState::getCrossoverHeroesFromPreviousScenar
auto campaignState = scenarioOps->campState; auto campaignState = scenarioOps->campState;
auto bonus = campaignState->getBonusForCurrentMap(); auto bonus = campaignState->getBonusForCurrentMap();
if (bonus && bonus->type == CScenarioTravel::STravelBonus::HEROES_FROM_PREVIOUS_SCENARIO) if(bonus && bonus->type == CScenarioTravel::STravelBonus::HEROES_FROM_PREVIOUS_SCENARIO)
{ {
std::vector<CGHeroInstance *> heroes; std::vector<CGHeroInstance *> heroes;
for(auto & node : campaignState->camp->scenarios[bonus->info2].crossoverHeroes) for(auto & node : campaignState->camp->scenarios[bonus->info2].crossoverHeroes)
@ -1172,12 +1172,8 @@ CGameState::CrossoverHeroesList CGameState::getCrossoverHeroesFromPreviousScenar
{ {
if(!campaignState->mapsConquered.empty()) if(!campaignState->mapsConquered.empty())
{ {
std::vector<CGHeroInstance *> heroes; std::vector<CGHeroInstance *> heroes = {};
for(auto & node : campaignState->camp->scenarios[campaignState->mapsConquered.back()].crossoverHeroes)
{
auto h = CCampaignState::crossoverDeserialize(node);
heroes.push_back(h);
}
crossoverHeroes.heroesFromAnyPreviousScenarios = crossoverHeroes.heroesFromPreviousScenario = heroes; crossoverHeroes.heroesFromAnyPreviousScenarios = crossoverHeroes.heroesFromPreviousScenario = heroes;
crossoverHeroes.heroesFromPreviousScenario = heroes; crossoverHeroes.heroesFromPreviousScenario = heroes;
@ -1190,7 +1186,7 @@ CGameState::CrossoverHeroesList CGameState::getCrossoverHeroesFromPreviousScenar
// remove heroes which didn't reached the end of the scenario, but were available at the start // remove heroes which didn't reached the end of the scenario, but were available at the start
for(auto hero : lostCrossoverHeroes) for(auto hero : lostCrossoverHeroes)
{ {
// auto hero = CCampaignState::crossoverDeserialize(node); // auto hero = CCampaignState::crossoverDeserialize(node);
vstd::erase_if(crossoverHeroes.heroesFromAnyPreviousScenarios, [hero](CGHeroInstance * h) vstd::erase_if(crossoverHeroes.heroesFromAnyPreviousScenarios, [hero](CGHeroInstance * h)
{ {
return hero->subID == h->subID; return hero->subID == h->subID;
@ -1202,11 +1198,12 @@ CGameState::CrossoverHeroesList CGameState::getCrossoverHeroesFromPreviousScenar
{ {
auto hero = CCampaignState::crossoverDeserialize(node); auto hero = CCampaignState::crossoverDeserialize(node);
// add new heroes and replace old heroes with newer ones // add new heroes and replace old heroes with newer ones
auto it = range::find_if(crossoverHeroes.heroesFromAnyPreviousScenarios, [hero](CGHeroInstance * h) auto it = range::find_if(crossoverHeroes.heroesFromAnyPreviousScenarios, [hero](CGHeroInstance * h)
{ {
return hero->subID == h->subID; return hero->subID == h->subID;
}); });
if (it != crossoverHeroes.heroesFromAnyPreviousScenarios.end())
if(it != crossoverHeroes.heroesFromAnyPreviousScenarios.end())
{ {
// replace old hero with newer one // replace old hero with newer one
crossoverHeroes.heroesFromAnyPreviousScenarios[it - crossoverHeroes.heroesFromAnyPreviousScenarios.begin()] = hero; crossoverHeroes.heroesFromAnyPreviousScenarios[it - crossoverHeroes.heroesFromAnyPreviousScenarios.begin()] = hero;
@ -1216,6 +1213,11 @@ CGameState::CrossoverHeroesList CGameState::getCrossoverHeroesFromPreviousScenar
// add new hero // add new hero
crossoverHeroes.heroesFromAnyPreviousScenarios.push_back(hero); crossoverHeroes.heroesFromAnyPreviousScenarios.push_back(hero);
} }
if(mapNr == campaignState->mapsConquered.back())
{
crossoverHeroes.heroesFromPreviousScenario.push_back(hero);
}
} }
} }
} }

View File

@ -719,46 +719,68 @@ bool CModHandler::checkDependencies(const std::vector <TModID> & input) const
return true; return true;
} }
std::vector <TModID> CModHandler::resolveDependencies(std::vector <TModID> modsToResolve) const // Returned vector affects the resource loaders call order (see CFilesystemList::load).
// The loaders call order matters when dependent mod overrides resources in its dependencies.
std::vector <TModID> CModHandler::validateAndSortDependencies(std::vector <TModID> modsToResolve) const
{ {
std::vector<TModID> brokenMods; // Topological sort algorithm.
// TODO: Investigate possible ways to improve performance.
boost::range::sort(modsToResolve); // Sort mods per name
std::vector <TModID> sortedValidMods; // Vector keeps order of elements (LIFO)
sortedValidMods.reserve(modsToResolve.size()); // push_back calls won't cause memory reallocation
std::set <TModID> resolvedModIDs; // Use a set for validation for performance reason, but set does not keep order of elements
auto looksValid = [&](const CModInfo & mod) -> bool // Mod is resolved if it has not dependencies or all its dependencies are already resolved
auto isResolved = [&](const CModInfo & mod) -> CModInfo::EValidationStatus
{ {
auto res = true; if(mod.dependencies.size() > resolvedModIDs.size())
return CModInfo::PENDING;
for(const TModID & dependency : mod.dependencies) for(const TModID & dependency : mod.dependencies)
{ {
if(!vstd::contains(modsToResolve, dependency)) if(!vstd::contains(resolvedModIDs, dependency))
{ return CModInfo::PENDING;
logMod->error("Mod '%s' will not work: it depends on mod '%s', which is not installed.", mod.name, dependency);
res = false; //continue iterations, since we should show all errors for the current mod.
}
} }
return res; return CModInfo::PASSED;
}; };
while(true) while(true)
{ {
for(auto mod : modsToResolve) std::set <TModID> resolvedOnCurrentTreeLevel;
for(auto it = modsToResolve.begin(); it != modsToResolve.end();) // One iteration - one level of mods tree
{ {
if(!looksValid(this->allMods.at(mod))) if(isResolved(allMods.at(*it)) == CModInfo::PASSED)
brokenMods.push_back(mod);
}
if(!brokenMods.empty())
{
vstd::erase_if(modsToResolve, [&](TModID mid)
{ {
return brokenMods.end() != std::find(brokenMods.begin(), brokenMods.end(), mid); resolvedOnCurrentTreeLevel.insert(*it); // Not to the resolvedModIDs, so current node childs will be resolved on the next iteration
}); sortedValidMods.push_back(*it);
brokenMods.clear(); it = modsToResolve.erase(it);
continue; continue;
}
it++;
} }
if(resolvedOnCurrentTreeLevel.size())
{
resolvedModIDs.insert(resolvedOnCurrentTreeLevel.begin(), resolvedOnCurrentTreeLevel.end());
continue;
}
// If there're no valid mods on the current mods tree level, no more mod can be resolved, should be end.
break; break;
} }
boost::range::sort(modsToResolve);
return modsToResolve; // Left mods have unresolved dependencies, output all to log.
for(const auto & brokenModID : modsToResolve)
{
const CModInfo & brokenMod = allMods.at(brokenModID);
for(const TModID & dependency : brokenMod.dependencies)
{
if(!vstd::contains(resolvedModIDs, dependency))
logMod->error("Mod '%s' will not work: it depends on mod '%s', which is not installed.", brokenMod.name, dependency);
}
}
return sortedValidMods;
} }
std::vector<std::string> CModHandler::getModList(std::string path) std::vector<std::string> CModHandler::getModList(std::string path)
{ {
std::string modDir = boost::to_upper_copy(path + "MODS/"); std::string modDir = boost::to_upper_copy(path + "MODS/");
@ -898,7 +920,7 @@ static ui32 calculateModChecksum(const std::string modName, ISimpleResourceLoade
void CModHandler::loadModFilesystems() void CModHandler::loadModFilesystems()
{ {
activeMods = resolveDependencies(activeMods); activeMods = validateAndSortDependencies(activeMods);
coreMod.updateChecksum(calculateModChecksum("core", CResourceHandler::get("core"))); coreMod.updateChecksum(calculateModChecksum("core", CResourceHandler::get("core")));

View File

@ -242,9 +242,15 @@ class DLL_LINKAGE CModHandler
// - circular dependencies // - circular dependencies
bool checkDependencies(const std::vector <TModID> & input) const; bool checkDependencies(const std::vector <TModID> & input) const;
// returns load order in which all dependencies are resolved, e.g. loaded after required mods /**
// function assumes that input list is valid (checkDependencies returned true) * 1. Set apart mods with resolved dependencies from mods which have unresolved dependencies
std::vector <TModID> resolveDependencies(std::vector<TModID> input) const; * 2. Sort resolved mods using topological algorithm
* 3. Log all problem mods and their unresolved dependencies
*
* @param modsToResolve list of valid mod IDs (checkDependencies returned true - TODO: Clarify it.)
* @return a vector of the topologically sorted resolved mods: child nodes (dependent mods) have greater index than parents
*/
std::vector <TModID> validateAndSortDependencies(std::vector <TModID> modsToResolve) const;
std::vector<std::string> getModList(std::string path); std::vector<std::string> getModList(std::string path);
void loadMods(std::string path, std::string parent, const JsonNode & modSettings, bool enableMods); void loadMods(std::string path, std::string parent, const JsonNode & modSettings, bool enableMods);

View File

@ -261,6 +261,28 @@ bool JsonNode::isCompact() const
} }
} }
bool JsonNode::TryBoolFromString(bool & success) const
{
success = true;
if(type == JsonNode::JsonType::DATA_BOOL)
return Bool();
success = type == JsonNode::JsonType::DATA_STRING;
if(success)
{
auto boolParamStr = String();
boost::algorithm::trim(boolParamStr);
boost::algorithm::to_lower(boolParamStr);
success = boolParamStr == "true";
if(success)
return true;
success = boolParamStr == "false";
}
return false;
}
void JsonNode::clear() void JsonNode::clear()
{ {
setType(JsonType::DATA_NULL); setType(JsonType::DATA_NULL);
@ -632,7 +654,17 @@ std::shared_ptr<ILimiter> JsonUtils::parseLimiter(const JsonNode & limiter)
{ {
creatureLimiter->setCreature(CreatureID(creature)); creatureLimiter->setCreature(CreatureID(creature));
}); });
creatureLimiter->includeUpgrades = parameters.size() > 1 ? parameters[1].Bool() : false; auto includeUpgrades = false;
if(parameters.size() > 1)
{
bool success = true;
includeUpgrades = parameters[1].TryBoolFromString(success);
if(!success)
logMod->error("Second parameter of '%s' limiter should be Bool", limiterType);
}
creatureLimiter->includeUpgrades = includeUpgrades;
return creatureLimiter; return creatureLimiter;
} }
else if(limiterType == "HAS_ANOTHER_BONUS_LIMITER") else if(limiterType == "HAS_ANOTHER_BONUS_LIMITER")

View File

@ -87,6 +87,9 @@ public:
/// removes all data from node and sets type to null /// removes all data from node and sets type to null
void clear(); void clear();
/// returns bool or bool equivalent of string value if 'success' is true, or false otherwise
bool TryBoolFromString(bool & success) const;
/// non-const accessors, node will change type on type mismatch /// non-const accessors, node will change type on type mismatch
bool & Bool(); bool & Bool();
double & Float(); double & Float();

View File

@ -654,7 +654,7 @@ namespace VCMIDirs
static bool initialized = false; static bool initialized = false;
if (!initialized) if (!initialized)
{ {
#ifndef VCMI_ANDROID #ifdef VCMI_WINDOWS
std::locale::global(boost::locale::generator().generate("en_US.UTF-8")); std::locale::global(boost::locale::generator().generate("en_US.UTF-8"));
#endif #endif
boost::filesystem::path::imbue(std::locale()); boost::filesystem::path::imbue(std::locale());

View File

@ -148,30 +148,43 @@ std::vector<JsonNode> CObjectClassesHandler::loadLegacyData(size_t dataSize)
/// selects preferred ID (or subID) for new object /// selects preferred ID (or subID) for new object
template<typename Map> template<typename Map>
si32 selectNextID(const JsonNode & fixedID, const Map & map, si32 defaultID) si32 selectNextID(const JsonNode & fixedID, const Map & map, si32 fixedObjectsBound)
{ {
if (!fixedID.isNull() && (si32)fixedID.Float() < defaultID) assert(fixedObjectsBound > 0);
return static_cast<si32>(fixedID.Float()); // H3M object with fixed ID if(fixedID.isNull())
{
auto lastID = map.empty() ? 0 : map.rbegin()->first;
return lastID < fixedObjectsBound ? fixedObjectsBound : lastID + 1;
}
auto id = static_cast<si32>(fixedID.Float());
if(id >= fixedObjectsBound)
logGlobal->error("Getting next ID overflowed: %d >= %d", id, fixedObjectsBound);
if (map.empty()) return id;
return defaultID; // no objects loaded, keep gap for H3M objects
if (map.rbegin()->first >= defaultID)
return map.rbegin()->first + 1; // some modded objects loaded, return next available
return defaultID; // some H3M objects loaded, first modded found
} }
void CObjectClassesHandler::loadObjectEntry(const std::string & identifier, const JsonNode & entry, ObjectContainter * obj) void CObjectClassesHandler::loadObjectEntry(const std::string & identifier, const JsonNode & entry, ObjectContainter * obj, bool isSubobject)
{ {
if (!handlerConstructors.count(obj->handlerName)) static const si32 fixedObjectsBound = 1000; // legacy value for backward compatibilitty
static const si32 fixedSubobjectsBound = 10000000; // large enough arbitrary value to avoid ID-collisions
si32 usedBound = fixedObjectsBound;
if(!handlerConstructors.count(obj->handlerName))
{ {
logGlobal->error("Handler with name %s was not found!", obj->handlerName); logGlobal->error("Handler with name %s was not found!", obj->handlerName);
return; return;
} }
const auto convertedId = VLC->modh->normalizeIdentifier(entry.meta, "core", identifier);
const auto & entryIndex = entry["index"];
bool useSelectNextID = !isSubobject || entryIndex.isNull();
std::string convertedId = VLC->modh->normalizeIdentifier(entry.meta, "core", identifier); if(useSelectNextID && isSubobject)
{
si32 id = selectNextID(entry["index"], obj->subObjects, 1000); usedBound = fixedSubobjectsBound;
logGlobal->error("Subobject index is Null. convertedId = '%s' obj->id = %d", convertedId, obj->id);
}
si32 id = useSelectNextID ? selectNextID(entryIndex, obj->subObjects, usedBound)
: (si32)entryIndex.Float();
auto handler = handlerConstructors.at(obj->handlerName)(); auto handler = handlerConstructors.at(obj->handlerName)();
handler->setType(obj->id, id); handler->setType(obj->id, id);
@ -196,46 +209,54 @@ void CObjectClassesHandler::loadObjectEntry(const std::string & identifier, cons
//some mods redefine content handlers in the decoration.json in such way: //some mods redefine content handlers in the decoration.json in such way:
//"core:sign" : { "types" : { "forgeSign" : { ... //"core:sign" : { "types" : { "forgeSign" : { ...
static const std::vector<std::string> knownProblemObjects static const std::vector<std::string> breakersRMG
{ {
"hota.hota decorations:hotaPandoraBox" "hota.hota decorations:hotaPandoraBox"
, "hota.hota decorations:hotaSubterreanGate" , "hota.hota decorations:hotaSubterreanGate"
}; };
bool overrideForce = !obj->subObjects.count(id) || const bool isExistingKey = obj->subObjects.count(id) > 0;
std::any_of(knownProblemObjects.begin(), knownProblemObjects.end(), [obj, id](const std::string & str) const bool isBreaker = std::any_of(breakersRMG.begin(), breakersRMG.end(),
{ [&handler](const std::string & str)
return str.compare(obj->subObjects[id]->subTypeName) == 0; {
}); return str.compare(handler->subTypeName) == 0;
});
const bool passedHandler = !isExistingKey && !isBreaker;
if(overrideForce) // DO NOT override mod handlers by default if(passedHandler)
{ {
obj->subObjects[id] = handler; obj->subObjects[id] = handler;
obj->subIds[convertedId] = id; obj->subIds[convertedId] = id;
} }
else if(isExistingKey) //It's supposed that fan mods handlers are not overridden by default handlers
{
logGlobal->trace("Handler '%s' has not been overridden with handler '%s' in object %s(%d)::%s(%d)",
obj->subObjects[id]->subTypeName, obj->handlerName, obj->identifier, obj->id, convertedId, id);
}
else else
{ {
logGlobal->warn("Don't override handler %s in object %s(%d)::%s(%d) subTypeName : %s" logGlobal->warn("Handler '%s' for object %s(%d)::%s(%d) has not been activated as RMG breaker",
, obj->handlerName, obj->identifier, obj->id, convertedId, id, obj->subObjects[id]->subTypeName); obj->handlerName, obj->identifier, obj->id, convertedId, id);
} }
} }
CObjectClassesHandler::ObjectContainter * CObjectClassesHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & name) CObjectClassesHandler::ObjectContainter * CObjectClassesHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & name)
{ {
auto obj = new ObjectContainter(); auto obj = new ObjectContainter();
static const si32 fixedObjectsBound = 256; //Legacy value for backward compatibility
obj->identifier = name; obj->identifier = name;
obj->name = json["name"].String(); obj->name = json["name"].String();
obj->handlerName = json["handler"].String(); obj->handlerName = json["handler"].String();
obj->base = json["base"]; obj->base = json["base"];
obj->id = selectNextID(json["index"], objects, 256); obj->id = selectNextID(json["index"], objects, fixedObjectsBound);
if(json["defaultAiValue"].isNull()) if(json["defaultAiValue"].isNull())
obj->groupDefaultAiValue = boost::none; obj->groupDefaultAiValue = boost::none;
else else
obj->groupDefaultAiValue = static_cast<boost::optional<si32>>(json["defaultAiValue"].Integer()); obj->groupDefaultAiValue = static_cast<boost::optional<si32>>(json["defaultAiValue"].Integer());
for (auto entry : json["types"].Struct()) for (auto entry : json["types"].Struct())
{
loadObjectEntry(entry.first, entry.second, obj); loadObjectEntry(entry.first, entry.second, obj);
}
return obj; return obj;
} }
@ -257,6 +278,8 @@ void CObjectClassesHandler::loadObject(std::string scope, std::string name, cons
void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNode config, si32 ID, boost::optional<si32> subID) void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNode config, si32 ID, boost::optional<si32> subID)
{ {
static const bool isSubObject = true;
config.setType(JsonNode::JsonType::DATA_STRUCT); // ensure that input is not NULL config.setType(JsonNode::JsonType::DATA_STRUCT); // ensure that input is not NULL
assert(objects.count(ID)); assert(objects.count(ID));
if (subID) if (subID)
@ -265,10 +288,8 @@ void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNo
assert(config["index"].isNull()); assert(config["index"].isNull());
config["index"].Float() = subID.get(); config["index"].Float() = subID.get();
} }
inheritNodeWithMeta(config, objects.at(ID)->base); inheritNodeWithMeta(config, objects.at(ID)->base);
loadObjectEntry(identifier, config, objects[ID], isSubObject);
loadObjectEntry(identifier, config, objects[ID]);
} }
void CObjectClassesHandler::removeSubObject(si32 ID, si32 subID) void CObjectClassesHandler::removeSubObject(si32 ID, si32 subID)

View File

@ -283,8 +283,9 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase
/// format: customNames[primaryID][secondaryID] -> name /// format: customNames[primaryID][secondaryID] -> name
std::map<si32, std::vector<std::string>> customNames; std::map<si32, std::vector<std::string>> customNames;
void loadObjectEntry(const std::string & identifier, const JsonNode & entry, ObjectContainter * obj); void loadObjectEntry(const std::string & identifier, const JsonNode & entry, ObjectContainter * obj, bool isSubobject = false);
ObjectContainter * loadFromJson(const std::string & scope, const JsonNode & json, const std::string & name); ObjectContainter * loadFromJson(const std::string & scope, const JsonNode & json, const std::string & name);
public: public:
CObjectClassesHandler(); CObjectClassesHandler();
~CObjectClassesHandler(); ~CObjectClassesHandler();

View File

@ -144,21 +144,24 @@ CDwellingInstanceConstructor::CDwellingInstanceConstructor()
void CDwellingInstanceConstructor::initTypeData(const JsonNode & input) void CDwellingInstanceConstructor::initTypeData(const JsonNode & input)
{ {
const JsonVector & levels = input["creatures"].Vector(); const JsonVector & levels = input["creatures"].Vector();
availableCreatures.resize(levels.size()); const auto totalLevels = levels.size();
for (size_t i=0; i<levels.size(); i++)
availableCreatures.resize(totalLevels);
for(auto currentLevel = 0; currentLevel < totalLevels; currentLevel++)
{ {
const JsonVector & creatures = levels[i].Vector(); const JsonVector & creaturesOnLevel = levels[currentLevel].Vector();
availableCreatures[i].resize(creatures.size()); const auto creaturesNumber = creaturesOnLevel.size();
for (size_t j=0; j<creatures.size(); j++) availableCreatures[currentLevel].resize(creaturesNumber);
for(auto currentCreature = 0; currentCreature < creaturesNumber; currentCreature++)
{ {
VLC->modh->identifiers.requestIdentifier("creature", creatures[j], [=] (si32 index) VLC->modh->identifiers.requestIdentifier("creature", creaturesOnLevel[currentCreature], [=] (si32 index)
{ {
availableCreatures[i][j] = VLC->creh->objects[index]; availableCreatures[currentLevel][currentCreature] = VLC->creh->objects[index];
}); });
} }
assert(!availableCreatures[i].empty()); assert(!availableCreatures[currentLevel].empty());
} }
guards = input["guards"]; guards = input["guards"];
} }

View File

@ -30,7 +30,7 @@ SHeroName::SHeroName() : heroId(-1)
PlayerInfo::PlayerInfo(): canHumanPlay(false), canComputerPlay(false), PlayerInfo::PlayerInfo(): canHumanPlay(false), canComputerPlay(false),
aiTactic(EAiTactic::RANDOM), isFactionRandom(false), hasRandomHero(false), mainCustomHeroPortrait(-1), mainCustomHeroId(-1), hasMainTown(false), aiTactic(EAiTactic::RANDOM), isFactionRandom(false), hasRandomHero(false), mainCustomHeroPortrait(-1), mainCustomHeroId(-1), hasMainTown(false),
generateHeroAtMainTown(false), team(TeamID::NO_TEAM), /* following are unused */ generateHero(false), p7(0), powerPlaceholders(-1) generateHeroAtMainTown(false), posOfMainTown(-1), team(TeamID::NO_TEAM), /* following are unused */ generateHero(false), p7(0), powerPlaceholders(-1)
{ {
allowedFactions = VLC->townh->getAllowedFactions(); allowedFactions = VLC->townh->getAllowedFactions();
} }

View File

@ -1,4 +1,4 @@
GNU GENERAL PUBLIC LICENSE  GNU GENERAL PUBLIC LICENSE
Version 2, June 1991 Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc., Copyright (C) 1989, 1991 Free Software Foundation, Inc.,

View File

@ -11,10 +11,16 @@ if(APPLE)
") ")
endif() endif()
# Manually fix VCMI library links in AI libraries with install_name_tool
install(CODE " install(CODE "
set(BU_CHMOD_BUNDLE_ITEMS ON) set(BU_CHMOD_BUNDLE_ITEMS ON)
include(BundleUtilities) include(BundleUtilities)
fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/${APP_BUNDLE_DIR}\" \"\" \"\") fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/${APP_BUNDLE_DIR}\" \"\" \"\")
execute_process(COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change \"libvcmi.dylib\" \"@executable_path/../Frameworks/libvcmi.dylib\" \"\${CMAKE_INSTALL_PREFIX}/${APP_BUNDLE_BINARY_DIR}/AI/libBattleAI.dylib\")
execute_process(COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change \"libvcmi.dylib\" \"@executable_path/../Frameworks/libvcmi.dylib\" \"\${CMAKE_INSTALL_PREFIX}/${APP_BUNDLE_BINARY_DIR}/AI/libEmptyAI.dylib\")
execute_process(COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change \"libvcmi.dylib\" \"@executable_path/../Frameworks/libvcmi.dylib\" \"\${CMAKE_INSTALL_PREFIX}/${APP_BUNDLE_BINARY_DIR}/AI/libStupidAI.dylib\")
execute_process(COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change \"libvcmi.dylib\" \"@executable_path/../Frameworks/libvcmi.dylib\" \"\${CMAKE_INSTALL_PREFIX}/${APP_BUNDLE_BINARY_DIR}/AI/libVCAI.dylib\")
execute_process(COMMAND rm \"\${CMAKE_INSTALL_PREFIX}/${APP_BUNDLE_BINARY_DIR}/libvcmi.dylib\")
" COMPONENT Runtime) " COMPONENT Runtime)
endif(APPLE) endif(APPLE)
@ -22,7 +28,7 @@ endif(APPLE)
if(WIN32) if(WIN32)
if(ENABLE_LAUNCHER) if(ENABLE_LAUNCHER)
# Temporary ugly fix for Qt deployment since windeployqt broken in Vcpkg # Temporary ugly fix for Qt deployment since windeployqt broken in Vcpkg
#there are some weird issues with variables used in path not evaluated properly when trying to remove code duplication from below lines #there are some weird issues with variables used in path not evaluated properly when trying to remove code duplication from below lines
if(EXISTS ${CMAKE_BINARY_DIR}/../../vcpkg) #current path to vcpkg main folder on appveyor CI if(EXISTS ${CMAKE_BINARY_DIR}/../../vcpkg) #current path to vcpkg main folder on appveyor CI
if(CMAKE_SIZEOF_VOID_P EQUAL 8) #64 bit build if(CMAKE_SIZEOF_VOID_P EQUAL 8) #64 bit build

View File

@ -1306,11 +1306,11 @@ void CGameHandler::handleClientDisconnection(std::shared_ptr<CConnection> c)
{ {
for(auto playerConns : connections) for(auto playerConns : connections)
{ {
for(auto conn : playerConns.second) for(auto i = playerConns.second.begin(); i != playerConns.second.end(); )
{ {
if(lobby->state != EServerState::SHUTDOWN && conn == c) if(lobby->state != EServerState::SHUTDOWN && *i == c)
{ {
vstd::erase_if_present(playerConns.second, conn); i = playerConns.second.erase(i);
if(playerConns.second.size()) if(playerConns.second.size())
continue; continue;
PlayerCheated pc; PlayerCheated pc;
@ -1319,6 +1319,8 @@ void CGameHandler::handleClientDisconnection(std::shared_ptr<CConnection> c)
sendAndApply(&pc); sendAndApply(&pc);
checkVictoryLossConditionsForPlayer(playerConns.first); checkVictoryLossConditionsForPlayer(playerConns.first);
} }
else
++i;
} }
} }
} }
@ -5426,7 +5428,8 @@ void CGameHandler::checkVictoryLossConditionsForAll()
void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player) void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
{ {
const PlayerState * p = getPlayerState(player); const PlayerState * p = getPlayerState(player);
if (p->status != EPlayerStatus::INGAME) return;
if(!p || p->status != EPlayerStatus::INGAME) return;
auto victoryLossCheckResult = gs->checkForVictoryAndLoss(player); auto victoryLossCheckResult = gs->checkForVictoryAndLoss(player);

View File

@ -90,11 +90,7 @@ set(test_SRCS
spells/targetConditions/ReceptiveFeatureConditionTest.cpp spells/targetConditions/ReceptiveFeatureConditionTest.cpp
spells/targetConditions/SpellEffectConditionTest.cpp spells/targetConditions/SpellEffectConditionTest.cpp
spells/targetConditions/TargetConditionItemFixture.cpp spells/targetConditions/TargetConditionItemFixture.cpp
vcai/mock_ResourceManager.cpp
vcai/mock_VCAI.cpp
vcai/ResurceManagerTest.cpp
mock/BattleFake.cpp mock/BattleFake.cpp
mock/mock_IGameCallback.cpp mock/mock_IGameCallback.cpp
mock/mock_MapService.cpp mock/mock_MapService.cpp
@ -147,16 +143,12 @@ set(mock_HEADERS
mock/mock_UnitInfo.h mock/mock_UnitInfo.h
mock/mock_vstd_RNG.h mock/mock_vstd_RNG.h
mock/mock_CPSICallback.h mock/mock_CPSICallback.h
vcai/mock_ResourceManager.h
vcai/mock_VCAI.h
vcai/mock_VCAI_CGoal.h
) )
add_subdirectory_with_folder("3rdparty" googletest EXCLUDE_FROM_ALL) add_subdirectory_with_folder("3rdparty" googletest EXCLUDE_FROM_ALL)
add_executable(vcmitest ${test_SRCS} ${test_HEADERS} ${mock_HEADERS}) add_executable(vcmitest ${test_SRCS} ${test_HEADERS} ${mock_HEADERS})
target_link_libraries(vcmitest PRIVATE gtest gmock vcmi ${SYSTEM_LIBS} VCAI) target_link_libraries(vcmitest PRIVATE gtest gmock vcmi ${SYSTEM_LIBS})
target_include_directories(vcmitest target_include_directories(vcmitest
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}

View File

@ -42,6 +42,12 @@ TEST(MapFormat, Random)
SCOPED_TRACE("MapFormat_Random start"); SCOPED_TRACE("MapFormat_Random start");
CMapGenOptions opt; CMapGenOptions opt;
CRmgTemplate tmpl;
const_cast<CRmgTemplate::CPlayerCountRange &>(tmpl.getCpuPlayers()).addRange(1, 4);
const_cast<CRmgTemplate::Zones &>(tmpl.getZones())[0] = std::make_shared<rmg::ZoneOptions>();
opt.setMapTemplate(&tmpl);
opt.setHeight(CMapHeader::MAP_SIZE_MIDDLE); opt.setHeight(CMapHeader::MAP_SIZE_MIDDLE);
opt.setWidth(CMapHeader::MAP_SIZE_MIDDLE); opt.setWidth(CMapHeader::MAP_SIZE_MIDDLE);
@ -84,7 +90,7 @@ static JsonNode getFromArchive(CZipLoader & archive, const std::string & archive
ResourceID resource(archiveFilename, EResType::TEXT); ResourceID resource(archiveFilename, EResType::TEXT);
if(!archive.existsResource(resource)) if(!archive.existsResource(resource))
throw std::runtime_error(archiveFilename+" not found"); throw std::runtime_error(archiveFilename + " not found");
auto data = archive.load(resource)->readAll(); auto data = archive.load(resource)->readAll();
@ -144,14 +150,14 @@ TEST(MapFormat, Objects)
{ {
static const std::string MAP_DATA_PATH = "test/ObjectPropertyTest/"; static const std::string MAP_DATA_PATH = "test/ObjectPropertyTest/";
const JsonNode initialHeader(ResourceID(MAP_DATA_PATH+CMapFormatJson::HEADER_FILE_NAME)); const JsonNode initialHeader(ResourceID(MAP_DATA_PATH + CMapFormatJson::HEADER_FILE_NAME));
const JsonNode expectedHeader(ResourceID(MAP_DATA_PATH+CMapFormatJson::HEADER_FILE_NAME));//same as initial for now const JsonNode expectedHeader(ResourceID(MAP_DATA_PATH + CMapFormatJson::HEADER_FILE_NAME));//same as initial for now
const JsonNode initialObjects(ResourceID(MAP_DATA_PATH+CMapFormatJson::OBJECTS_FILE_NAME)); const JsonNode initialObjects(ResourceID(MAP_DATA_PATH + CMapFormatJson::OBJECTS_FILE_NAME));
const JsonNode expectedObjects(ResourceID(MAP_DATA_PATH+"objects.ex.json")); const JsonNode expectedObjects(ResourceID(MAP_DATA_PATH + "objects.ex.json"));
const JsonNode expectedSurface(ResourceID(MAP_DATA_PATH+"surface_terrain.json")); const JsonNode expectedSurface(ResourceID(MAP_DATA_PATH + "surface_terrain.json"));
const JsonNode expectedUnderground(ResourceID(MAP_DATA_PATH+"underground_terrain.json")); const JsonNode expectedUnderground(ResourceID(MAP_DATA_PATH + "underground_terrain.json"));
std::unique_ptr<CMap> originalMap = loadOriginal(initialHeader, initialObjects, expectedSurface, expectedUnderground); std::unique_ptr<CMap> originalMap = loadOriginal(initialHeader, initialObjects, expectedSurface, expectedUnderground);
@ -183,11 +189,11 @@ TEST(MapFormat, Terrain)
{ {
static const std::string MAP_DATA_PATH = "test/TerrainTest/"; static const std::string MAP_DATA_PATH = "test/TerrainTest/";
const JsonNode expectedHeader(ResourceID(MAP_DATA_PATH+CMapFormatJson::HEADER_FILE_NAME)); const JsonNode expectedHeader(ResourceID(MAP_DATA_PATH + CMapFormatJson::HEADER_FILE_NAME));
const JsonNode expectedObjects(ResourceID(MAP_DATA_PATH+CMapFormatJson::OBJECTS_FILE_NAME)); const JsonNode expectedObjects(ResourceID(MAP_DATA_PATH + CMapFormatJson::OBJECTS_FILE_NAME));
const JsonNode expectedSurface(ResourceID(MAP_DATA_PATH+"surface_terrain.json")); const JsonNode expectedSurface(ResourceID(MAP_DATA_PATH + "surface_terrain.json"));
const JsonNode expectedUnderground(ResourceID(MAP_DATA_PATH+"underground_terrain.json")); const JsonNode expectedUnderground(ResourceID(MAP_DATA_PATH + "underground_terrain.json"));
std::unique_ptr<CMap> originalMap = loadOriginal(expectedHeader, expectedObjects, expectedSurface, expectedUnderground); std::unique_ptr<CMap> originalMap = loadOriginal(expectedHeader, expectedObjects, expectedSurface, expectedUnderground);

View File

@ -194,6 +194,7 @@ TEST_F(TargetConditionTest, StoresNormalLevelCondition)
TEST_F(TargetConditionTest, StoresReceptiveFeature) TEST_F(TargetConditionTest, StoresReceptiveFeature)
{ {
redirectFactoryToStub();
auto itemStub = std::make_shared<StrictMock<ItemMock>>(); auto itemStub = std::make_shared<StrictMock<ItemMock>>();
EXPECT_CALL(factoryMock, createReceptiveFeature()).WillOnce(Return(itemStub)); EXPECT_CALL(factoryMock, createReceptiveFeature()).WillOnce(Return(itemStub));
setupSubject(JsonNode()); setupSubject(JsonNode());