Merge remote-tracking branch 'upstream/master' into sasquatch-prebuild-maint

This commit is contained in:
Mike Dickson 2022-05-10 14:32:31 -04:00
commit 718e3f121b
85 changed files with 705 additions and 604 deletions

View file

@ -215,6 +215,58 @@ namespace OpenSim.Region.CoreModules.World.Sound
});
}
public virtual void TriggerCollisionSound(
UUID soundId, UUID ownerID, UUID objectID, UUID parentID, double gain, Vector3 position, UInt64 handle)
{
float radius;
SceneObjectPart part;
ScenePresence ssp = null;
if (!m_scene.TryGetSceneObjectPart(objectID, out part))
{
if (!m_scene.TryGetScenePresence(ownerID, out ssp))
return;
if (!ssp.ParcelAllowThisAvatarSounds)
return;
radius = MaxDistance;
}
else
{
SceneObjectGroup grp = part.ParentGroup;
if (grp.IsAttachment)
{
if (!m_scene.TryGetScenePresence(grp.AttachedAvatar, out ssp))
return;
if (!ssp.ParcelAllowThisAvatarSounds)
return;
}
radius = (float)part.SoundRadius;
if (radius == 0)
{
radius = MaxDistance;
part.SoundRadius = MaxDistance;
}
}
radius *= radius;
m_scene.ForEachRootScenePresence(delegate (ScenePresence sp)
{
if(sp.MuteCollisions)
return;
if (Vector3.DistanceSquared(sp.AbsolutePosition, position) > radius) // Max audio distance
return;
sp.ControllingClient.SendTriggeredSound(soundId, ownerID,
objectID, parentID, handle, position,
(float)gain);
});
}
public virtual void StopSound(UUID objectID)
{
SceneObjectPart m_host;

View file

@ -67,6 +67,9 @@ namespace OpenSim.Region.Framework.Interfaces
UUID soundId, UUID ownerID, UUID objectID, UUID parentID,
double gain, Vector3 position, UInt64 handle);
void TriggerCollisionSound(
UUID soundId, UUID ownerID, UUID objectID, UUID parentID,
double gain, Vector3 position, UInt64 handle);
/// <summary>
/// Stop sounds eminating from an object.
/// </summary>

View file

@ -265,7 +265,7 @@ namespace OpenSim.Region.Framework.Scenes
int thisMatScaled = thisMaterial * MaxMaterials;
// bool doneownsound = false;
// bool doneownsound = false;
CollisionForSoundInfo colInfo;
uint id;

View file

@ -2916,19 +2916,19 @@ namespace OpenSim.Region.Framework.Scenes
if (soundID.IsZero())
return;
int now = Util.EnvironmentTickCount();
if (Util.EnvironmentTickCountSubtract(now, LastColSoundSentTime) < 200)
return;
ISoundModule soundModule = ParentGroup.Scene.RequestModuleInterface<ISoundModule>();
if (soundModule == null)
return;
if (volume > 1)
volume = 1;
if (volume < 0)
else if (volume < 0)
volume = 0;
int now = Util.EnvironmentTickCount();
if(Util.EnvironmentTickCountSubtract(now, LastColSoundSentTime) < 200)
return;
LastColSoundSentTime = now;
UUID ownerID = OwnerID;
@ -2936,7 +2936,7 @@ namespace OpenSim.Region.Framework.Scenes
UUID parentID = ParentGroup.UUID;
ulong regionHandle = ParentGroup.Scene.RegionInfo.RegionHandle;
soundModule.TriggerSound(soundID, ownerID, objectID, parentID, volume, position, regionHandle);
soundModule.TriggerCollisionSound(soundID, ownerID, objectID, parentID, volume, position, regionHandle);
}
public void PhysicsOutOfBounds(Vector3 pos)

View file

@ -70,16 +70,24 @@ namespace OpenSim.Region.Framework.Scenes
public ScriptControlled eventControls;
}
enum AgentUpdateFlags: byte
{
None = 0,
HideTitle = 0x01,
CliAutoPilot = 0x02,
MuteCollisions = 0x80
}
public delegate void SendCoarseLocationsMethod(UUID scene, ScenePresence presence, List<Vector3> coarseLocations, List<UUID> avatarUUIDs);
public class ScenePresence : EntityBase, IScenePresence, IDisposable
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
// ~ScenePresence()
// {
// m_log.DebugFormat("[SCENE PRESENCE]: Destructor called on {0}", Name);
// }
//~ScenePresence()
//{
// m_log.DebugFormat("[SCENE PRESENCE]: Destructor called on {0}", Name);
//}
public bool GotAttachmentsData = false;
public int EnvironmentVersion = -1;
@ -135,6 +143,9 @@ namespace OpenSim.Region.Framework.Scenes
}
}
public bool HideTitle;
public bool MuteCollisions;
private ScenePresenceStateMachine m_stateMachine;
/// <summary>
@ -2636,6 +2647,15 @@ namespace OpenSim.Region.Framework.Scenes
ACFlags allFlags = (ACFlags)agentData.ControlFlags;
bool newHideTitle = (agentData.Flags & (byte)AgentUpdateFlags.HideTitle) != 0;
if(HideTitle != newHideTitle)
{
HideTitle = newHideTitle;
SendAvatarDataToAllAgents();
}
MuteCollisions = (agentData.Flags & (byte)AgentUpdateFlags.MuteCollisions) != 0;
// FIXME: This does not work as intended because the viewer only sends the lbutton down when the button
// is first pressed, not whilst it is held down. If this is required in the future then need to look
// for an AGENT_CONTROL_LBUTTON_UP event and make sure to handle cases where an initial DOWN is not
@ -2661,17 +2681,13 @@ namespace OpenSim.Region.Framework.Scenes
ACFlags flags = RemoveIgnoredControls(allFlags, IgnoredControls);
m_AgentControlFlags = flags;
if (AllowMovement)
Rotation = agentData.BodyRotation;
// Raycast from the avatar's head to the camera to see if there's anything blocking the view
// this exclude checks may not be complete
if (agentData.NeedsCameraCollision)
checkCameraCollision();
PhysicsActor actor = m_physActor;
// This will be the case if the agent is sitting on the groudn or on an object.
PhysicsActor actor = m_physActor;
if (actor == null)
{
SendControlsToScripts((uint)allFlags);
@ -2680,6 +2696,8 @@ namespace OpenSim.Region.Framework.Scenes
if (AllowMovement)
{
Rotation = agentData.BodyRotation;
//m_log.DebugFormat("[SCENE PRESENCE]: Initial body rotation {0} for {1}", agentData.BodyRotation, Name);
bool update_movementflag = false;
bool DCFlagKeyPressed = false;

File diff suppressed because it is too large Load diff

View file

@ -126,7 +126,7 @@ namespace OpenSim.Region.PhysicsModule.ubOde
// private string m_name = String.Empty;
// other filter control
int m_colliderfilter = 0;
public int Colliderfilter = 0;
int m_colliderGroundfilter = 0;
int m_colliderObjectfilter = 0;
@ -373,18 +373,18 @@ namespace OpenSim.Region.PhysicsModule.ubOde
{
if (value)
{
m_colliderfilter += 3;
if (m_colliderfilter > 9)
m_colliderfilter = 9;
Colliderfilter += 3;
if (Colliderfilter > 9)
Colliderfilter = 9;
}
else
{
m_colliderfilter--;
if (m_colliderfilter < 0)
m_colliderfilter = 0;
Colliderfilter--;
if (Colliderfilter < 0)
Colliderfilter = 0;
}
if (m_colliderfilter < 6)
if (Colliderfilter < 6)
m_iscolliding = false;
else
{
@ -440,7 +440,6 @@ namespace OpenSim.Region.PhysicsModule.ubOde
}
set
{
// Ubit filter this also
if (value)
{
m_colliderObjectfilter += 2;
@ -1282,7 +1281,7 @@ namespace OpenSim.Region.PhysicsModule.ubOde
if (m_colliderGroundfilter > 2)
{
m_iscolliding = true;
m_colliderfilter = 2;
Colliderfilter = 2;
if (m_colliderGroundfilter > 10)
{
@ -2022,7 +2021,7 @@ namespace OpenSim.Region.PhysicsModule.ubOde
_zeroFlag = false;
m_targetVelocity = Vector3.Zero;
m_freemove = true;
m_colliderfilter = -1;
Colliderfilter = int.MinValue;
m_colliderObjectfilter = -1;
m_colliderGroundfilter = -1;

View file

@ -56,7 +56,7 @@ namespace OpenSim.Region.PhysicsModule.ubOde
}
if (Util.IsWindows())
Util.LoadArchSpecificWindowsDll("ode.dll");
Util.LoadArchSpecificWindowsDll("ubode.dll");
SafeNativeMethods.InitODE();

View file

@ -1003,27 +1003,31 @@ namespace OpenSim.Region.PhysicsModule.ubOde
if (chr == null)
continue;
chr.IsColliding = false;
// chr.CollidingGround = false; not done here
chr.CollidingObj = false;
if (chr.Colliderfilter < -1)
chr.Colliderfilter = -1;
else
{
chr.IsColliding = false;
// chr.CollidingGround = false; not done here
chr.CollidingObj = false;
if(chr.Body == IntPtr.Zero || chr.collider == IntPtr.Zero )
continue;
// do colisions with static space
SafeNativeMethods.SpaceCollide2(chr.collider, StaticSpace, IntPtr.Zero, nearCallback);
if(chr.Body == IntPtr.Zero || chr.collider == IntPtr.Zero )
continue;
// do colisions with static space
SafeNativeMethods.SpaceCollide2(chr.collider, StaticSpace, IntPtr.Zero, nearCallback);
SafeNativeMethods.SpaceCollide2(chr.collider, CharsSpace, IntPtr.Zero, nearCallback);
SafeNativeMethods.SpaceCollide2(chr.collider, ActiveSpace, IntPtr.Zero, nearCallback);
}
// no coll with gnd
}
// chars with chars
SafeNativeMethods.SpaceCollide(CharsSpace, IntPtr.Zero, nearCallback);
//SafeNativeMethods.SpaceCollide(CharsSpace, IntPtr.Zero, nearCallback);
}
catch (AccessViolationException)
{
m_log.Warn("[PHYSICS]: Unable to collide Character to static space");
}
}
lock (_activeprims)
@ -1067,6 +1071,7 @@ namespace OpenSim.Region.PhysicsModule.ubOde
m_log.Warn("[PHYSICS]: Unable to collide in Active: " + e.Message);
}
/*
// and with chars
try
{
@ -1076,6 +1081,7 @@ namespace OpenSim.Region.PhysicsModule.ubOde
{
m_log.Warn("[PHYSICS]: Unable to collide Active to Character: " + e.Message);
}
*/
}
#endregion

View file

@ -679,7 +679,10 @@ namespace OpenSim.Region.ScriptEngine.Shared
get
{
if (m_data == null)
{
m_data=new object[0];
return 0;
}
return m_data.Length;
}
}
@ -923,26 +926,16 @@ namespace OpenSim.Region.ScriptEngine.Shared
public static bool operator ==(list a, list b)
{
int la = -1;
int lb = -1;
try { la = a.Length; }
catch (NullReferenceException) { }
try { lb = b.Length; }
catch (NullReferenceException) { }
return la == lb;
if (b is null)
return (a is null);
return (a is null) ? false : a.Length == b.Length;
}
public static bool operator !=(list a, list b)
{
int la = -1;
int lb = -1;
try { la = a.Length; }
catch (NullReferenceException) { }
try { lb = b.Length; }
catch (NullReferenceException) { }
return la != lb;
if (b is null)
return !(a is null);
return (a is null) ? true : a.Length != b.Length;
}
public void Add(object o)

View file

@ -105,33 +105,19 @@ namespace OpenSim.Region.ScriptEngine.Yengine
public void Save(LSL_List lis)
{
if (lis == null)
usage = instance.UpdateLocalsHeapUse(usage, 0);
else
usage = instance.UpdateLocalsHeapUse(usage, Size(lis));
usage = (lis == null) ? instance.UpdateLocalsHeapUse(usage, 0) : instance.UpdateLocalsHeapUse(usage, lis.Size);
value = lis;
}
public void Restore(LSL_List lis)
{
value = lis;
if (lis != null)
usage = Size(lis);
else
usage = 0;
usage = (lis is null) ? 0 : lis.Size;
}
//private static int counter = 5;
public static int Size(LSL_List lis)
{
try
{
return lis.Size;
}
catch
{
return 0;
}
return lis is null ? 0 : lis.Size;
}
}

View file

@ -237,7 +237,10 @@ namespace OpenSim.Region.ScriptEngine.Yengine
if (liss != null)
{
foreach (LSL_List lis in liss)
newheapuse += HeapTrackerList.Size(lis);
{
if(!(lis is null))
newheapuse += lis.Size;
}
iarLists = liss;
}
else
@ -322,7 +325,10 @@ namespace OpenSim.Region.ScriptEngine.Yengine
if(iarLists != null)
{
foreach(LSL_List lis in iarLists)
newheapuse -= HeapTrackerList.Size(lis);
{
if (!(lis is null))
newheapuse += lis.Size;
}
iarLists = null;
}
if(iarObjects != null)

View file

@ -204,7 +204,7 @@ namespace OpenSim.Services.GridService
if (!string.IsNullOrEmpty(configVal))
m_ExtraFeatures["GridStatusRSS"] = configVal;
m_ExtraFeatures["ExportSupported"] = gridConfig.GetString("ExportSupported", "true");
m_ExtraFeatures["ExportSupported"] = gridConfig.GetBoolean("ExportSupported", true);
string[] sections = new string[] { "Const, Startup", "Hypergrid", "GatekeeperService" };
string gatekeeperURIAlias = Util.GetConfigVarFromSections<string>(config, "GatekeeperURIAlias", sections, string.Empty);

View file

@ -174,53 +174,39 @@ namespace OpenSim.Services.UserAccountService
private GridUserInfo ToInfo(GridUserData d)
{
GridUserInfo info = new GridUserInfo();
info.UserID = d.UserID;
Dictionary<string, string> kvp = d.Data;
GridUserInfo info = new GridUserInfo() { UserID = d.UserID };
string tmpstr;
Dictionary<string, string> kvp = d.Data;
if (kvp.TryGetValue("HomeRegionID", out tmpstr))
info.HomeRegionID = new UUID(tmpstr);
else
info.HomeRegionID = UUID.Zero;
UUID.TryParse(tmpstr, out info.HomeRegionID);
if (kvp.TryGetValue("HomePosition", out tmpstr))
info.HomePosition = Vector3.Parse(tmpstr);
else
info.HomePosition = Vector3.Zero;
Vector3.TryParse(tmpstr, out info.HomePosition);
if (kvp.TryGetValue("HomeLookAt", out tmpstr))
info.HomeLookAt = Vector3.Parse(tmpstr);
else
info.HomeLookAt = Vector3.Zero;
Vector3.TryParse(tmpstr, out info.HomeLookAt);
if (kvp.TryGetValue("LastRegionID", out tmpstr))
info.LastRegionID = new UUID(tmpstr);
else
info.LastRegionID = UUID.Zero;
UUID.TryParse(tmpstr, out info.LastRegionID);
if (kvp.TryGetValue("LastPosition", out tmpstr))
info.LastPosition = Vector3.Parse(tmpstr);
else
info.LastPosition = Vector3.Zero;
Vector3.TryParse(tmpstr, out info.LastPosition);
if (kvp.TryGetValue("LastLookAt", out tmpstr))
info.LastLookAt = Vector3.Parse(tmpstr);
else
info.LastLookAt = Vector3.Zero;
Vector3.TryParse(tmpstr, out info.LastLookAt);
if (kvp.TryGetValue("Online", out tmpstr))
info.Online = bool.Parse(tmpstr);
else
info.Online = false;
bool.TryParse(tmpstr, out info.Online);
if (kvp.TryGetValue("Login", out tmpstr))
info.Login = Util.ToDateTime(Convert.ToInt32(tmpstr));
if (kvp.TryGetValue("Login", out tmpstr) && Int32.TryParse(tmpstr, out int login))
info.Login = Util.ToDateTime(login);
else
info.Login = Util.UnixEpoch;
if (kvp.TryGetValue("Logout", out tmpstr))
info.Logout = Util.ToDateTime(Convert.ToInt32(tmpstr));
if (kvp.TryGetValue("Logout", out tmpstr) && Int32.TryParse(tmpstr, out int logout))
info.Logout = Util.ToDateTime(logout);
else
info.Logout = Util.UnixEpoch;
@ -287,10 +273,13 @@ namespace OpenSim.Services.UserAccountService
d.Data["LastPosition"] = lastPosition.ToString();
d.Data["LastLookAt"] = lastLookAt.ToString();
bool ret = m_Database.Store(d);
if (ret && userID.Length >= 36)
cache.Add(userID.Substring(0, 36), d, 300000);
return ret;
if(m_Database.Store(d))
{
if (userID.Length >= 36)
cache.Add(userID.Substring(0, 36), d, 300000);
return true;
}
return false;
}
public bool SetHome(string userID, UUID homeID, Vector3 homePosition, Vector3 homeLookAt)
@ -307,10 +296,13 @@ namespace OpenSim.Services.UserAccountService
d.Data["HomePosition"] = homePosition.ToString();
d.Data["HomeLookAt"] = homeLookAt.ToString();
bool ret = m_Database.Store(d);
if (ret && userID.Length >= 36)
cache.Add(userID.Substring(0, 36), d, 300000);
return ret;
if(m_Database.Store(d))
{
if (userID.Length >= 36)
cache.Add(userID.Substring(0, 36), d, 300000);
return true;
}
return false;
}
public bool SetLastPosition(string userID, UUID sessionID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt)
@ -329,10 +321,13 @@ namespace OpenSim.Services.UserAccountService
d.Data["LastPosition"] = lastPosition.ToString();
d.Data["LastLookAt"] = lastLookAt.ToString();
bool ret = m_Database.Store(d);
if (ret && userID.Length >= 36)
cache.Add(userID.Substring(0, 36), d, 300000);
return ret;
if(m_Database.Store(d))
{
if (userID.Length >= 36)
cache.Add(userID.Substring(0, 36), d, 300000);
return true;
}
return false;
}
}
}

View file

@ -100,6 +100,7 @@ namespace Prebuild.Core
bool m_PauseAfterFinish;
string[] m_ProjectGroups;
public HashSet<string> excludeFolders = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
#endregion
#region Constructors
@ -673,7 +674,7 @@ namespace Prebuild.Core
}
m_Clean = m_CommandLine["clean"];
string removeDirs = m_CommandLine["removedir"];
if (removeDirs != null && removeDirs.Length == 0)
if (!string.IsNullOrEmpty(removeDirs))
{
m_RemoveDirectories = removeDirs.Split('|');
}
@ -685,6 +686,20 @@ namespace Prebuild.Core
}
m_PauseAfterFinish = m_CommandLine.WasPassed("pause");
string excludeDirstr = m_CommandLine["excludedir"];
if (!string.IsNullOrEmpty(excludeDirstr))
{
string[] ex = excludeDirstr.Split(new char[] {'|'}, StringSplitOptions.RemoveEmptyEntries);
if(ex.Length > 0)
{
foreach(string s in ex)
{
string st = s.Trim();
if(st.Length > 0)
excludeFolders.Add(st);
}
}
}
//LoadSchema();
}

View file

@ -261,6 +261,9 @@ namespace Prebuild.Core.Nodes
// a significant performance hit when running on a network drive.
if (str.EndsWith(".svn"))
continue;
string dname = Path.GetFileName(str);
if(Kernel.Instance.excludeFolders.Contains(dname))
continue;
RecurseDirectories(Helper.NormalizePath(str), pattern, recurse, useRegex, exclusions);
}
@ -345,7 +348,6 @@ namespace Prebuild.Core.Nodes
throw new WarningException("Could not compile regex pattern: {0}", ex.Message);
}
foreach (XmlNode child in node.ChildNodes)
{
IDataNode dataNode = Kernel.Instance.ParseNode(child, this);

View file

@ -1,7 +1,5 @@
<configuration>
<dllmap os="osx" dll="ode" target="lib64/libode.dylib" />
<dllmap os="!windows,osx" cpu="x86-64,ia64" dll="ode" target="lib64/libode-x86_64" />
<dllmap os="!windows,osx" cpu="x86" dll="ode" target="lib32/libode" />
<dllmap os="!windows,osx" cpu="ppc64" dll="ode" target="lib64/libode-ppc64" />
<dllmap os="!windows,osx" cpu="s390x" dll="ode" target="lib64/libode-s390x" />
<dllmap os="osx" dll="ubode" target="lib64/libubode.dylib" />
<dllmap os="!windows,osx" cpu="x86-64,ia64" dll="ubode" target="lib64/libubode-x86_64" />
<dllmap os="!windows,osx" cpu="x86" dll="ubode" target="lib32/libubode" />
</configuration>

Binary file not shown.

View file

@ -343,4 +343,5 @@
<Key Name="name" Value="snd_SandStep" />
<Key Name="assetType" Value="1" />
<Key Name="fileName" Value="snd_SandStep.ogg" />
</Section></Nini>
</Section>
</Nini>

BIN
bin/lib32/libubode.dylib Executable file

Binary file not shown.

BIN
bin/lib32/libubode.so Executable file

Binary file not shown.

BIN
bin/lib32/ubode.dll Executable file

Binary file not shown.

BIN
bin/lib64/libubode-x86_64.so Executable file

Binary file not shown.

BIN
bin/lib64/libubode.dylib Executable file

Binary file not shown.

BIN
bin/lib64/ubode.dll Executable file

Binary file not shown.

View file

@ -1,6 +1,6 @@
@echo OFF
bin\Prebuild.exe /target vs2019 /file prebuild.xml
bin\Prebuild.exe /target vs2015 /excludedir = "obj | bin"
setlocal ENABLEEXTENSIONS
set VALUE_NAME=MSBuildToolsPath
@ -17,8 +17,34 @@ for %%e in (Enterprise Professional Community) do (
)
)
rem try find vs2017
for %%e in (Enterprise Professional Community) do (
if exist "%PROGRAMS%\Microsoft Visual Studio\2017\%%e\MSBuild\15.0\Bin\MSBuild.exe" (
@echo msbuild for at least VS2019 not found, please install a (Community) edition of VS2019
set ValueValue="%PROGRAMS%\Microsoft Visual Studio\2017\%%e\MSBuild\15.0\Bin\MSBuild"
goto :found
)
)
rem We have to use grep or find to locate the correct line, because reg query spits
rem out 4 lines before Windows 7 but 2 lines after Windows 7.
rem We use grep if it's on the path; otherwise we use the built-in find command
rem from Windows. (We must use grep on Cygwin because it overrides the "find" command.)
for %%X in (grep.exe) do (set FOUNDGREP=%%~$PATH:X)
if defined FOUNDGREP (
set FINDCMD=grep
) else (
set FINDCMD=find
)
rem try vs2015
FOR /F "usebackq tokens=1-3" %%A IN (`REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0" /v %VALUE_NAME% 2^>nul ^| %FINDCMD% "%VALUE_NAME%"`) DO (
set ValueValue=%%C\msbuild
goto :found
)
@echo msbuild for at least VS2015 not found, please install a (Community) edition of VS2017 or VS2015
@echo Not creating compile.bat
if exist "compile.bat" (
del compile.bat
@ -36,4 +62,4 @@ rem @echo %ValueValue% /p:Configuration=Release opensim.sln > compile.bat
if exist "bin\addin-db-002" (
del /F/Q/S bin\addin-db-002 > NUL
rmdir /Q/S bin\addin-db-002
)
)

View file

@ -5,7 +5,6 @@ case "$1" in
'clean')
mono bin/Prebuild.exe /clean
;;
@ -16,11 +15,13 @@ case "$1" in
;;
*)
mono bin/Prebuild.exe /target vs2019 /file prebuild.xml
mono bin/Prebuild.exe /target nant
mono bin/Prebuild.exe /target vs2015 /excludedir = "obj | bin"
;;
esac
rm -fr bin/addin-db-002

View file

@ -1,6 +1,6 @@
@echo OFF
bin\Prebuild.exe /target vs2019 /targetframework v4_8
bin\Prebuild.exe /target vs2019 /targetframework v4_8 /excludedir = "obj | bin"
setlocal ENABLEEXTENSIONS
set VALUE_NAME=MSBuildToolsPath

View file

@ -19,7 +19,7 @@ case "$1" in
*)
mono bin/Prebuild.exe /target vs2019 /targetframework v4_8
mono bin/Prebuild.exe /target vs2019 /targetframework v4_8 /excludedir = "obj | bin"
;;