mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2025-12-20 00:08:38 +00:00
Compare commits
202 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14e3f01faa | ||
|
|
46c0c5c3ff | ||
|
|
bebddb0b9a | ||
|
|
74f82eedcd | ||
|
|
f8e96b2173 | ||
|
|
68d6ae715a | ||
|
|
ae4a695de3 | ||
|
|
16f84c019b | ||
|
|
c6c63a7928 | ||
|
|
8ef60a6316 | ||
|
|
82aebaf457 | ||
|
|
3c9ab82b7d | ||
|
|
5d0a3f5ebe | ||
|
|
9d5ccbca27 | ||
|
|
90af55e6f4 | ||
|
|
a11b2025e9 | ||
|
|
67a3b5a745 | ||
|
|
42079006a0 | ||
|
|
efb4aa70f4 | ||
|
|
d4684d123a | ||
|
|
0d38c2b1f4 | ||
|
|
4938bcf673 | ||
|
|
33eba37ed2 | ||
|
|
602d22fa9c | ||
|
|
795757ab0a | ||
|
|
5d69059e0b | ||
|
|
89ef81c5a8 | ||
|
|
3bf60d2762 | ||
|
|
d9c5f7e044 | ||
|
|
1d8b01e166 | ||
|
|
45cbb59144 | ||
|
|
0c5a05ab1c | ||
|
|
a81d02bd46 | ||
|
|
3191ef4f4d | ||
|
|
5c6bd0eb6f | ||
|
|
6b4634746a | ||
|
|
cdd08ad14b | ||
|
|
b64e630cbe | ||
|
|
197539f353 | ||
|
|
bf89ba7638 | ||
|
|
9f4a6e6e67 | ||
|
|
9718245793 | ||
|
|
9af542597c | ||
|
|
6391a5f79c | ||
|
|
20c0ba28ef | ||
|
|
f01740a3db | ||
|
|
a89c6e630c | ||
|
|
0b37323a5f | ||
|
|
14799c2733 | ||
|
|
031386ffb9 | ||
|
|
b561c3fc17 | ||
|
|
644f95e96c | ||
|
|
864c909760 | ||
|
|
aacf491a0c | ||
|
|
5ce219eb6e | ||
|
|
f1578a5174 | ||
|
|
1d7dbe3791 | ||
|
|
5c121bee75 | ||
|
|
6be0cff505 | ||
|
|
6281e258da | ||
|
|
68110f2038 | ||
|
|
f423f35c62 | ||
|
|
56246d3853 | ||
|
|
f6cbd67206 | ||
|
|
720214016b | ||
|
|
55d7ccd28b | ||
|
|
6d1ea0b20c | ||
|
|
40e8d85359 | ||
|
|
e397b7f7cc | ||
|
|
4090b40ac7 | ||
|
|
05df0ee725 | ||
|
|
eaf56d0221 | ||
|
|
1fc9b96f83 | ||
|
|
462020f842 | ||
|
|
b4e8e24921 | ||
|
|
84129da691 | ||
|
|
141673409f | ||
|
|
2ad10a5946 | ||
|
|
495b7594f9 | ||
|
|
1727492e07 | ||
|
|
82e38d78eb | ||
|
|
55af650da5 | ||
|
|
a062e98f2d | ||
|
|
8b8671c679 | ||
|
|
5e4f6a7926 | ||
|
|
57b2c8f1cf | ||
|
|
02e7f2144f | ||
|
|
c13fd10bd8 | ||
|
|
7fe0b4499b | ||
|
|
94160bdc42 | ||
|
|
c01c0f7cd1 | ||
|
|
65b06ae3b9 | ||
|
|
5d443f3ed2 | ||
|
|
6a6c903d7d | ||
|
|
52f5ef2a39 | ||
|
|
b92a4ea505 | ||
|
|
8ae9db3a34 | ||
|
|
388c72052c | ||
|
|
e965899395 | ||
|
|
5605e04f0a | ||
|
|
953103719c | ||
|
|
ea53fd0a87 | ||
|
|
f6770ea1c9 | ||
|
|
561b2a3566 | ||
|
|
d24e6d0302 | ||
|
|
98491a4562 | ||
|
|
08d7b9deb0 | ||
|
|
b904b1062f | ||
|
|
ad187c0d88 | ||
|
|
6a035f4832 | ||
|
|
021e8d2ad9 | ||
|
|
61609574d0 | ||
|
|
7b7f1518a6 | ||
|
|
6aa5a633e4 | ||
|
|
6f80ee9faa | ||
|
|
99e8df318c | ||
|
|
2ea334c62e | ||
|
|
27d0b7427a | ||
|
|
62c20c331e | ||
|
|
51d59f0f66 | ||
|
|
ab90c9dc68 | ||
|
|
f7c8b871b3 | ||
|
|
678b32f318 | ||
|
|
62221fd6b8 | ||
|
|
f932be1395 | ||
|
|
d8fd1035c3 | ||
|
|
09e8a52811 | ||
|
|
0b1032199b | ||
|
|
3e5dfb3174 | ||
|
|
4ada8595bd | ||
|
|
bc64c43748 | ||
|
|
f4f7935d4c | ||
|
|
aacc0a1720 | ||
|
|
e5ee909b96 | ||
|
|
f3f444d0bd | ||
|
|
43d18b6d49 | ||
|
|
f24be67c8c | ||
|
|
6ce4f2941b | ||
|
|
ef5398fae8 | ||
|
|
ce600fdf42 | ||
|
|
7a6485775d | ||
|
|
d39e7a32d6 | ||
|
|
b4cbe85b36 | ||
|
|
4d83417c9d | ||
|
|
25bfbf4862 | ||
|
|
e539e1bdc7 | ||
|
|
b0f83fcd6d | ||
|
|
4880a4e60b | ||
|
|
1beb2e01d5 | ||
|
|
a57dc7387c | ||
|
|
0289f65072 | ||
|
|
7bb3b38ea4 | ||
|
|
54d1b8211b | ||
|
|
f97ffc76b5 | ||
|
|
39891b835d | ||
|
|
c3d6a6103e | ||
|
|
32e005bde3 | ||
|
|
2db65d324a | ||
|
|
65c2b81d0a | ||
|
|
a216eb48af | ||
|
|
6f1e230b81 | ||
|
|
15ec01f447 | ||
|
|
f564cc5ebb | ||
|
|
be349d0557 | ||
|
|
93bc8373f8 | ||
|
|
4b11f61ea1 | ||
|
|
060db81f38 | ||
|
|
c27deb20a8 | ||
|
|
b231abf19f | ||
|
|
f17f2959b7 | ||
|
|
9f7886df3f | ||
|
|
ecc0dac4d9 | ||
|
|
e2f9ebc715 | ||
|
|
93716218a5 | ||
|
|
db149d16a7 | ||
|
|
6d22b19ef3 | ||
|
|
0f6cf10179 | ||
|
|
9a745ffb97 | ||
|
|
df95c6c6b0 | ||
|
|
9711187940 | ||
|
|
f009f1b834 | ||
|
|
f4187e194e | ||
|
|
32b2ea9fb7 | ||
|
|
6e87ee3718 | ||
|
|
a718d1cefe | ||
|
|
019f347be4 | ||
|
|
5b2b00a9f0 | ||
|
|
e6e1b6f061 | ||
|
|
88a7d2e7e9 | ||
|
|
b223fc2d97 | ||
|
|
589f4f5746 | ||
|
|
c4e7bbbf0c | ||
|
|
bbb43600e5 | ||
|
|
c602654800 | ||
|
|
7a49dc5188 | ||
|
|
f1b84e57e4 | ||
|
|
3db5830f7b | ||
|
|
4acc71485e | ||
|
|
7822210cc0 | ||
|
|
c46eee4499 | ||
|
|
ad09b1dec9 | ||
|
|
e640f82712 |
51
.travis.yml
51
.travis.yml
@@ -16,41 +16,48 @@ branches:
|
||||
mono: none
|
||||
|
||||
# ASF requires .NET Core 2.0+
|
||||
# TODO: We should target stable 2.0.0 once it's released
|
||||
dotnet: 2.0.0-preview1-005977
|
||||
dotnet: 2.0.0
|
||||
|
||||
env:
|
||||
global:
|
||||
- CONFIGURATION: Release
|
||||
- DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
- DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
|
||||
- RUNTIMES="generic win-x64 linux-x64 linux-arm osx-x64" # https://github.com/travis-ci/travis-ci/issues/1444
|
||||
|
||||
before_script: dotnet restore
|
||||
|
||||
script:
|
||||
- set -e
|
||||
- dotnet restore
|
||||
- dotnet build -c Release
|
||||
- dotnet publish -c Release -o out/generic
|
||||
- echo "generic" > "ArchiSteamFarm/out/generic/ArchiSteamFarm.version"
|
||||
- dotnet publish -c Release -r win-x64 -o out/win-x64
|
||||
- echo "win-x64" > "ArchiSteamFarm/out/win-x64/ArchiSteamFarm.version"
|
||||
- dotnet publish -c Release -r linux-x64 -o out/linux-x64
|
||||
- echo "linux-x64" > "ArchiSteamFarm/out/linux-x64/ArchiSteamFarm.version"
|
||||
- dotnet publish -c Release -r linux-arm -o out/linux-arm
|
||||
- echo "linux-arm" > "ArchiSteamFarm/out/linux-arm/ArchiSteamFarm.version"
|
||||
- dotnet publish -c Release -r osx-x64 -o out/osx-x64
|
||||
- echo "osx-x64" > "ArchiSteamFarm/out/osx-x64/ArchiSteamFarm.version"
|
||||
- |
|
||||
set -e
|
||||
|
||||
dotnet build -c "$CONFIGURATION" -o 'out/source' --no-restore /nologo
|
||||
dotnet test ArchiSteamFarm.Tests -c "$CONFIGURATION" -o 'out/source' --no-build --no-restore
|
||||
|
||||
publish() {
|
||||
if [ "$1" = 'generic' ]; then
|
||||
dotnet publish ArchiSteamFarm -c "$CONFIGURATION" -o "out/${1}" --no-restore /nologo
|
||||
else
|
||||
dotnet publish ArchiSteamFarm -c "$CONFIGURATION" -o "out/${1}" -r "$1" --no-restore /nologo
|
||||
fi
|
||||
|
||||
echo "$1" > "ArchiSteamFarm/out/${1}/ArchiSteamFarm.version"
|
||||
}
|
||||
|
||||
for RUNTIME in $RUNTIMES; do
|
||||
publish "$RUNTIME" &
|
||||
done
|
||||
wait
|
||||
|
||||
# This is our main build matrix
|
||||
matrix:
|
||||
# We can use fast finish, as we don't need to wait for all builds to mark it as failed/passed
|
||||
# We can use fast finish, as we don't need to wait for allow_failures builds to mark build as success
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
# We allow OS X to fail until https://github.com/travis-ci/travis-ci/issues/7757 is fixed
|
||||
- os: osx
|
||||
include:
|
||||
# We're building ASF with dotnet on latest versions of Linux and OS X
|
||||
# Ref: https://docs.travis-ci.com/user/ci-environment/#Virtualization-environments
|
||||
# We're building ASF with dotnet on latest versions of Linux and OS X
|
||||
- os: linux
|
||||
# Ref: https://docs.travis-ci.com/user/reference/trusty/
|
||||
dist: trusty
|
||||
sudo: false
|
||||
- os: osx
|
||||
# Ref: https://docs.travis-ci.com/user/reference/osx/
|
||||
osx_image: xcode9
|
||||
|
||||
BIN
ArchiSteamFarm.Tests/ASF.ico
Normal file
BIN
ArchiSteamFarm.Tests/ASF.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 281 KiB |
40
ArchiSteamFarm.Tests/ArchiSteamFarm.Tests.csproj
Normal file
40
ArchiSteamFarm.Tests/ArchiSteamFarm.Tests.csproj
Normal file
@@ -0,0 +1,40 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<ErrorReport>none</ErrorReport>
|
||||
<ApplicationIcon>ASF.ico</ApplicationIcon>
|
||||
<Copyright>Copyright © ArchiSteamFarm 2015-2017</Copyright>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;linux-arm;osx-x64</RuntimeIdentifiers>
|
||||
<Description>ASF is an application that allows you to farm steam cards using multiple steam accounts simultaneously.</Description>
|
||||
<Authors>JustArchi</Authors>
|
||||
<Company>JustArchi</Company>
|
||||
<PackageLicenseUrl>http://www.apache.org/licenses/LICENSE-2.0</PackageLicenseUrl>
|
||||
<PackageProjectUrl>https://github.com/JustArchi/ArchiSteamFarm</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/JustArchi/ArchiSteamFarm.git</RepositoryUrl>
|
||||
<PackageIconUrl>https://github.com/JustArchi/ArchiSteamFarm/raw/master/resources/ASF.ico</PackageIconUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DebugType>none</DebugType>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0-preview-20170810-02" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="1.2.0-beta3" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="1.2.0-beta3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ArchiSteamFarm\ArchiSteamFarm.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
175
ArchiSteamFarm.Tests/Trading.cs
Normal file
175
ArchiSteamFarm.Tests/Trading.cs
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2017 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using ArchiSteamFarm.JSON;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace ArchiSteamFarm.Tests {
|
||||
[TestClass]
|
||||
public sealed class Trading {
|
||||
[TestMethod]
|
||||
public void TradingMultiGameBadReject() {
|
||||
Steam.Item item1Game1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 1, 570, Steam.Item.EType.TradingCard);
|
||||
Steam.Item item1Game1X9 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 9, 570, Steam.Item.EType.TradingCard);
|
||||
Steam.Item item2Game1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 2, 1, 570, Steam.Item.EType.TradingCard);
|
||||
|
||||
Steam.Item item1Game2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 3, 1, 730, Steam.Item.EType.TradingCard);
|
||||
Steam.Item item2Game2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 4, 1, 730, Steam.Item.EType.TradingCard);
|
||||
|
||||
HashSet<Steam.Item> inventory = new HashSet<Steam.Item> { item1Game1X9, item1Game2, item2Game2 };
|
||||
HashSet<Steam.Item> itemsToGive = new HashSet<Steam.Item> { item1Game1, item1Game2 };
|
||||
HashSet<Steam.Item> itemsToReceive = new HashSet<Steam.Item> { item2Game1, item2Game2 };
|
||||
|
||||
Assert.IsFalse(AcceptsTrade(inventory, itemsToGive, itemsToReceive));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TradingMultiGameMultiTypeBadReject() {
|
||||
Steam.Item item1Type1Game1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 1, 570, Steam.Item.EType.TradingCard);
|
||||
Steam.Item item1Type1Game1X9 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 9, 570, Steam.Item.EType.TradingCard);
|
||||
Steam.Item item2Type1Game1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 2, 1, 570, Steam.Item.EType.TradingCard);
|
||||
|
||||
Steam.Item item3Type2Game2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 3, 1, 730, Steam.Item.EType.Emoticon);
|
||||
Steam.Item item3Type2Game2X9 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 3, 9, 730, Steam.Item.EType.Emoticon);
|
||||
Steam.Item item4Type2Game2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 4, 1, 730, Steam.Item.EType.Emoticon);
|
||||
|
||||
HashSet<Steam.Item> inventory = new HashSet<Steam.Item> { item1Type1Game1X9, item3Type2Game2X9, item4Type2Game2 };
|
||||
HashSet<Steam.Item> itemsToGive = new HashSet<Steam.Item> { item1Type1Game1, item4Type2Game2 };
|
||||
HashSet<Steam.Item> itemsToReceive = new HashSet<Steam.Item> { item2Type1Game1, item3Type2Game2 };
|
||||
|
||||
Assert.IsFalse(AcceptsTrade(inventory, itemsToGive, itemsToReceive));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TradingMultiGameMultiTypeNeutralAccept() {
|
||||
Steam.Item item1Type1Game1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 1, 570, Steam.Item.EType.TradingCard);
|
||||
Steam.Item item1Type1Game1X9 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 9, 570, Steam.Item.EType.TradingCard);
|
||||
Steam.Item item2Type1Game1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 2, 1, 570, Steam.Item.EType.TradingCard);
|
||||
|
||||
Steam.Item item3Type2Game2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 3, 1, 730, Steam.Item.EType.Emoticon);
|
||||
Steam.Item item4Type2Game2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 4, 1, 730, Steam.Item.EType.Emoticon);
|
||||
|
||||
HashSet<Steam.Item> inventory = new HashSet<Steam.Item> { item1Type1Game1X9, item3Type2Game2 };
|
||||
HashSet<Steam.Item> itemsToGive = new HashSet<Steam.Item> { item1Type1Game1, item3Type2Game2 };
|
||||
HashSet<Steam.Item> itemsToReceive = new HashSet<Steam.Item> { item2Type1Game1, item4Type2Game2 };
|
||||
|
||||
Assert.IsTrue(AcceptsTrade(inventory, itemsToGive, itemsToReceive));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TradingMultiGameNeutralAccept() {
|
||||
Steam.Item item1Game1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 1, 570, Steam.Item.EType.TradingCard);
|
||||
Steam.Item item1Game1X2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 2, 570, Steam.Item.EType.TradingCard);
|
||||
Steam.Item item2Game1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 2, 1, 570, Steam.Item.EType.TradingCard);
|
||||
|
||||
Steam.Item item1Game2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 1, 730, Steam.Item.EType.TradingCard);
|
||||
Steam.Item item2Game2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 2, 1, 730, Steam.Item.EType.TradingCard);
|
||||
|
||||
HashSet<Steam.Item> inventory = new HashSet<Steam.Item> { item1Game1X2, item1Game2 };
|
||||
HashSet<Steam.Item> itemsToGive = new HashSet<Steam.Item> { item1Game1, item1Game2 };
|
||||
HashSet<Steam.Item> itemsToReceive = new HashSet<Steam.Item> { item2Game1, item2Game2 };
|
||||
|
||||
Assert.IsTrue(AcceptsTrade(inventory, itemsToGive, itemsToReceive));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TradingSingleGameBadReject() {
|
||||
Steam.Item item1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 1, 570, Steam.Item.EType.TradingCard);
|
||||
Steam.Item item2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 2, 1, 570, Steam.Item.EType.TradingCard);
|
||||
|
||||
HashSet<Steam.Item> inventory = new HashSet<Steam.Item> { item1, item2 };
|
||||
HashSet<Steam.Item> itemsToGive = new HashSet<Steam.Item> { item1 };
|
||||
HashSet<Steam.Item> itemsToReceive = new HashSet<Steam.Item> { item2 };
|
||||
|
||||
Assert.IsFalse(AcceptsTrade(inventory, itemsToGive, itemsToReceive));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TradingSingleGameGoodAccept() {
|
||||
Steam.Item item1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 1, 570, Steam.Item.EType.TradingCard);
|
||||
Steam.Item item1X2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 2, 570, Steam.Item.EType.TradingCard);
|
||||
Steam.Item item2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 2, 1, 570, Steam.Item.EType.TradingCard);
|
||||
|
||||
HashSet<Steam.Item> inventory = new HashSet<Steam.Item> { item1X2 };
|
||||
HashSet<Steam.Item> itemsToGive = new HashSet<Steam.Item> { item1 };
|
||||
HashSet<Steam.Item> itemsToReceive = new HashSet<Steam.Item> { item2 };
|
||||
|
||||
Assert.IsTrue(AcceptsTrade(inventory, itemsToGive, itemsToReceive));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TradingSingleGameMultiTypeBadReject() {
|
||||
Steam.Item item1Type1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 1, 570, Steam.Item.EType.TradingCard);
|
||||
Steam.Item item1Type1X9 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 9, 570, Steam.Item.EType.TradingCard);
|
||||
Steam.Item item2Type1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 2, 1, 570, Steam.Item.EType.TradingCard);
|
||||
|
||||
Steam.Item item3Type2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 3, 1, 570, Steam.Item.EType.Emoticon);
|
||||
Steam.Item item3Type2X9 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 3, 9, 570, Steam.Item.EType.Emoticon);
|
||||
Steam.Item item4Type2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 4, 1, 570, Steam.Item.EType.Emoticon);
|
||||
|
||||
HashSet<Steam.Item> inventory = new HashSet<Steam.Item> { item1Type1X9, item3Type2X9, item4Type2 };
|
||||
HashSet<Steam.Item> itemsToGive = new HashSet<Steam.Item> { item1Type1, item4Type2 };
|
||||
HashSet<Steam.Item> itemsToReceive = new HashSet<Steam.Item> { item2Type1, item3Type2 };
|
||||
|
||||
Assert.IsFalse(AcceptsTrade(inventory, itemsToGive, itemsToReceive));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TradingSingleGameMultiTypeNeutralAccept() {
|
||||
Steam.Item item1Type1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 1, 570, Steam.Item.EType.TradingCard);
|
||||
Steam.Item item1Type1X9 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 9, 570, Steam.Item.EType.TradingCard);
|
||||
Steam.Item item2Type1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 2, 1, 570, Steam.Item.EType.TradingCard);
|
||||
|
||||
Steam.Item item3Type2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 3, 1, 570, Steam.Item.EType.Emoticon);
|
||||
Steam.Item item4Type2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 4, 1, 570, Steam.Item.EType.Emoticon);
|
||||
|
||||
HashSet<Steam.Item> inventory = new HashSet<Steam.Item> { item1Type1X9, item3Type2 };
|
||||
HashSet<Steam.Item> itemsToGive = new HashSet<Steam.Item> { item1Type1, item3Type2 };
|
||||
HashSet<Steam.Item> itemsToReceive = new HashSet<Steam.Item> { item2Type1, item4Type2 };
|
||||
|
||||
Assert.IsTrue(AcceptsTrade(inventory, itemsToGive, itemsToReceive));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TradingSingleGameNeutralAccept() {
|
||||
Steam.Item item1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 1, 570, Steam.Item.EType.TradingCard);
|
||||
Steam.Item item2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 2, 1, 570, Steam.Item.EType.TradingCard);
|
||||
|
||||
HashSet<Steam.Item> inventory = new HashSet<Steam.Item> { item1 };
|
||||
HashSet<Steam.Item> itemsToGive = new HashSet<Steam.Item> { item1 };
|
||||
HashSet<Steam.Item> itemsToReceive = new HashSet<Steam.Item> { item2 };
|
||||
|
||||
Assert.IsTrue(AcceptsTrade(inventory, itemsToGive, itemsToReceive));
|
||||
}
|
||||
|
||||
private static bool AcceptsTrade(HashSet<Steam.Item> inventory, HashSet<Steam.Item> itemsToGive, HashSet<Steam.Item> itemsToReceive) {
|
||||
Type trading = typeof(ArchiSteamFarm.Trading);
|
||||
MethodInfo method = trading.GetMethod("IsTradeNeutralOrBetter", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
|
||||
return (bool) method.Invoke(null, new object[] { inventory, itemsToGive, itemsToReceive });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26608.5
|
||||
VisualStudioVersion = 15.0.26621.2
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ArchiSteamFarm", "ArchiSteamFarm\ArchiSteamFarm.csproj", "{CF84911C-2C4C-4195-8AF3-ABBB6D3DE9AA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArchiSteamFarm.Tests", "ArchiSteamFarm.Tests\ArchiSteamFarm.Tests.csproj", "{91DC4C4F-3C23-4716-8CB2-BC7EAB65D759}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -15,8 +17,15 @@ Global
|
||||
{CF84911C-2C4C-4195-8AF3-ABBB6D3DE9AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CF84911C-2C4C-4195-8AF3-ABBB6D3DE9AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CF84911C-2C4C-4195-8AF3-ABBB6D3DE9AA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{91DC4C4F-3C23-4716-8CB2-BC7EAB65D759}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{91DC4C4F-3C23-4716-8CB2-BC7EAB65D759}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{91DC4C4F-3C23-4716-8CB2-BC7EAB65D759}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{91DC4C4F-3C23-4716-8CB2-BC7EAB65D759}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {D7D54143-C857-4B76-A219-0E98C5BC4895}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALLOW_COMMENT_AFTER_LBRACE/@EntryValue">False</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue">END_OF_LINE</s:String>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AFTER_CONTROL_TRANSFER_STATEMENTS/@EntryValue">1</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_FIELD/@EntryValue">0</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_FIELD/@EntryValue">1</s:Int64>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/CASE_BLOCK_BRACES/@EntryValue">END_OF_LINE</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/EMPTY_BLOCK_STYLE/@EntryValue">TOGETHER_SAME_LINE</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INDENT_STYLE/@EntryValue">Tab</s:String>
|
||||
@@ -384,4 +384,5 @@
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EXml_002ECodeStyle_002EFormatSettingsUpgrade_002EXmlMoveToCommonFormatterSettingsUpgrade/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EXml_002ECodeStyle_002EFormatSettingsUpgrade_002EXmlMoveToCommonFormatterSettingsUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Int64 x:Key="/Default/Environment/UnitTesting/ParallelProcessesCount/@EntryValue">8</s:Int64></wpf:ResourceDictionary>
|
||||
@@ -37,41 +37,16 @@ using ArchiSteamFarm.Localization;
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class ASF {
|
||||
private const byte AutoUpdatePeriodInHours = 24;
|
||||
private const string DefaultVersion = "source"; // Default entry of ArchiSteamFarm.version
|
||||
|
||||
internal static readonly ArchiLogger ArchiLogger = new ArchiLogger(SharedInfo.ASF);
|
||||
|
||||
private static readonly ConcurrentDictionary<Bot, DateTime> LastWriteTimes = new ConcurrentDictionary<Bot, DateTime>();
|
||||
private static readonly ConcurrentDictionary<string, DateTime> LastWriteTimes = new ConcurrentDictionary<string, DateTime>();
|
||||
|
||||
private static Timer AutoUpdatesTimer;
|
||||
private static FileSystemWatcher FileSystemWatcher;
|
||||
|
||||
internal static async Task CheckForUpdate(bool updateOverride = false) {
|
||||
string targetDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||
|
||||
// Cleanup from previous update - update directory for old in-use runtime files
|
||||
string backupDirectory = Path.Combine(targetDirectory, SharedInfo.UpdateDirectory);
|
||||
if (Directory.Exists(backupDirectory)) {
|
||||
// It's entirely possible that old process is still running, wait at least a second for eventual cleanup
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
|
||||
try {
|
||||
Directory.Delete(backupDirectory, true);
|
||||
} catch (Exception e) {
|
||||
ArchiLogger.LogGenericException(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup from previous update - old non-runtime in-use files
|
||||
foreach (string file in Directory.GetFiles(targetDirectory, "*.old", SearchOption.AllDirectories)) {
|
||||
try {
|
||||
File.Delete(file);
|
||||
} catch (Exception e) {
|
||||
ArchiLogger.LogGenericException(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Program.GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.None) {
|
||||
return;
|
||||
}
|
||||
@@ -102,11 +77,15 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
version = version.TrimEnd();
|
||||
if (string.IsNullOrEmpty(version) || !IsVersionValid(version)) {
|
||||
if (string.IsNullOrEmpty(version) || !IsValidVersion(version)) {
|
||||
ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, SharedInfo.VersionFile));
|
||||
return;
|
||||
}
|
||||
|
||||
if (version.Equals(DefaultVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((AutoUpdatesTimer == null) && Program.GlobalConfig.AutoUpdates) {
|
||||
TimeSpan autoUpdatePeriod = TimeSpan.FromHours(AutoUpdatePeriodInHours);
|
||||
|
||||
@@ -120,13 +99,39 @@ namespace ArchiSteamFarm {
|
||||
ArchiLogger.LogGenericInfo(string.Format(Strings.AutoUpdateCheckInfo, autoUpdatePeriod.ToHumanReadable()));
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericInfo(Strings.UpdateCheckingNewVersion);
|
||||
|
||||
string targetDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||
|
||||
// Cleanup from previous update - update directory for old in-use runtime files
|
||||
string backupDirectory = Path.Combine(targetDirectory, SharedInfo.UpdateDirectory);
|
||||
if (Directory.Exists(backupDirectory)) {
|
||||
// It's entirely possible that old process is still running, wait a short moment for eventual cleanup
|
||||
await Task.Delay(5000).ConfigureAwait(false);
|
||||
|
||||
try {
|
||||
Directory.Delete(backupDirectory, true);
|
||||
} catch (Exception e) {
|
||||
ArchiLogger.LogGenericException(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup from previous update - old non-runtime in-use files
|
||||
foreach (string file in Directory.EnumerateFiles(targetDirectory, "*.old", SearchOption.AllDirectories)) {
|
||||
try {
|
||||
File.Delete(file);
|
||||
} catch (Exception e) {
|
||||
ArchiLogger.LogGenericException(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string releaseURL = SharedInfo.GithubReleaseURL;
|
||||
if (Program.GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable) {
|
||||
releaseURL += "/latest";
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericInfo(Strings.UpdateCheckingNewVersion);
|
||||
|
||||
GitHub.ReleaseResponse releaseResponse;
|
||||
|
||||
if (Program.GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable) {
|
||||
@@ -189,7 +194,7 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericInfo(Strings.UpdateDownloadingNewVersion);
|
||||
ArchiLogger.LogGenericInfo(string.Format(Strings.UpdateDownloadingNewVersion, newVersion, binaryAsset.Size / 1024 / 1024));
|
||||
|
||||
byte[] result = await Program.WebBrowser.UrlGetToBytesRetry(binaryAsset.DownloadURL).ConfigureAwait(false);
|
||||
if (result == null) {
|
||||
@@ -205,6 +210,13 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsUnixVersion(version)) {
|
||||
string executable = Path.Combine(targetDirectory, SharedInfo.AssemblyName);
|
||||
if (File.Exists(executable)) {
|
||||
OS.UnixSetFileAccessExecutable(executable);
|
||||
}
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericInfo(Strings.UpdateFinished);
|
||||
await RestartOrExit().ConfigureAwait(false);
|
||||
}
|
||||
@@ -214,17 +226,10 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
// Before attempting to connect, initialize our list of CMs
|
||||
await Bot.InitializeCMs(Program.GlobalDatabase.CellID, Program.GlobalDatabase.ServerListProvider).ConfigureAwait(false);
|
||||
|
||||
foreach (string botName in Directory.EnumerateFiles(SharedInfo.ConfigDirectory, "*.json").Select(Path.GetFileNameWithoutExtension).Where(botName => !string.IsNullOrEmpty(botName) && (botName[0] != '.'))) {
|
||||
switch (botName) {
|
||||
case SharedInfo.ASF:
|
||||
case "example":
|
||||
case "minimal":
|
||||
continue;
|
||||
}
|
||||
// Before attempting to connect, initialize our configuration
|
||||
await Bot.InitializeSteamConfiguration(Program.GlobalConfig.SteamProtocols, Program.GlobalDatabase.CellID, Program.GlobalDatabase.ServerListProvider).ConfigureAwait(false);
|
||||
|
||||
foreach (string botName in Directory.EnumerateFiles(SharedInfo.ConfigDirectory, "*.json").Select(Path.GetFileNameWithoutExtension).Where(botName => !string.IsNullOrEmpty(botName) && IsValidBotName(botName)).OrderBy(botName => botName)) {
|
||||
Bot.RegisterBot(botName);
|
||||
}
|
||||
|
||||
@@ -270,15 +275,14 @@ namespace ArchiSteamFarm {
|
||||
Bot.RegisterBot(botName);
|
||||
}
|
||||
|
||||
private static bool IsVersionValid(string version) {
|
||||
private static bool IsUnixVersion(string version) {
|
||||
if (string.IsNullOrEmpty(version)) {
|
||||
ArchiLogger.LogNullError(nameof(version));
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (version) {
|
||||
case "generic":
|
||||
case "win-x64":
|
||||
case "linux-arm":
|
||||
case "linux-x64":
|
||||
case "osx-x64":
|
||||
return true;
|
||||
@@ -287,6 +291,45 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsValidBotName(string botName) {
|
||||
if (string.IsNullOrEmpty(botName)) {
|
||||
ArchiLogger.LogNullError(nameof(botName));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (botName[0] == '.') {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (botName) {
|
||||
case SharedInfo.ASF:
|
||||
case "example":
|
||||
case "minimal":
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsValidVersion(string version) {
|
||||
if (string.IsNullOrEmpty(version)) {
|
||||
ArchiLogger.LogNullError(nameof(version));
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (version) {
|
||||
case DefaultVersion:
|
||||
case "generic":
|
||||
case "linux-arm":
|
||||
case "linux-x64":
|
||||
case "osx-x64":
|
||||
case "win-x64":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static async void OnChanged(object sender, FileSystemEventArgs e) {
|
||||
if ((sender == null) || (e == null)) {
|
||||
ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e));
|
||||
@@ -298,6 +341,32 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
DateTime lastWriteTime = DateTime.UtcNow;
|
||||
|
||||
if (LastWriteTimes.TryGetValue(botName, out DateTime savedLastWriteTime)) {
|
||||
if (savedLastWriteTime >= lastWriteTime) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LastWriteTimes[botName] = lastWriteTime;
|
||||
|
||||
// It's entirely possible that some process is still accessing our file, allow at least a second before trying to read it
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
|
||||
// It's also possible that we got some other event in the meantime
|
||||
if (LastWriteTimes.TryGetValue(botName, out savedLastWriteTime)) {
|
||||
if (lastWriteTime != savedLastWriteTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (LastWriteTimes.TryRemove(botName, out savedLastWriteTime)) {
|
||||
if (lastWriteTime != savedLastWriteTime) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (botName.Equals(SharedInfo.ASF)) {
|
||||
ArchiLogger.LogGenericInfo(Strings.GlobalConfigChanged);
|
||||
await RestartOrExit().ConfigureAwait(false);
|
||||
@@ -305,35 +374,13 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
if (!Bot.Bots.TryGetValue(botName, out Bot bot)) {
|
||||
if (IsValidBotName(botName)) {
|
||||
await CreateBot(botName).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
DateTime lastWriteTime = File.GetLastWriteTime(e.FullPath);
|
||||
|
||||
if (LastWriteTimes.TryGetValue(bot, out DateTime savedLastWriteTime)) {
|
||||
if (savedLastWriteTime >= lastWriteTime) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LastWriteTimes[bot] = lastWriteTime;
|
||||
|
||||
// It's entirely possible that some process is still accessing our file, allow at least a second before trying to read it
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
|
||||
// It's also possible that we got some other event in the meantime
|
||||
if (LastWriteTimes.TryGetValue(bot, out savedLastWriteTime)) {
|
||||
if (lastWriteTime != savedLastWriteTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (LastWriteTimes.TryRemove(bot, out savedLastWriteTime)) {
|
||||
if (lastWriteTime != savedLastWriteTime) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await bot.OnNewConfigLoaded(new BotConfigEventArgs(BotConfig.Load(e.FullPath))).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -343,18 +390,58 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
string botName = Path.GetFileNameWithoutExtension(e.Name);
|
||||
await OnCreatedFile(e.Name, e.FullPath).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async Task<bool> OnCreatedFile(string name, string fullPath) {
|
||||
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(fullPath)) {
|
||||
ArchiLogger.LogNullError(nameof(name) + " || " + nameof(fullPath));
|
||||
return false;
|
||||
}
|
||||
|
||||
string botName = Path.GetFileNameWithoutExtension(name);
|
||||
if (string.IsNullOrEmpty(botName) || (botName[0] == '.')) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
DateTime lastWriteTime = DateTime.UtcNow;
|
||||
|
||||
if (LastWriteTimes.TryGetValue(botName, out DateTime savedLastWriteTime)) {
|
||||
if (savedLastWriteTime >= lastWriteTime) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
LastWriteTimes[botName] = lastWriteTime;
|
||||
|
||||
// It's entirely possible that some process is still accessing our file, allow at least a second before trying to read it
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
|
||||
// It's also possible that we got some other event in the meantime
|
||||
if (LastWriteTimes.TryGetValue(botName, out savedLastWriteTime)) {
|
||||
if (lastWriteTime != savedLastWriteTime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (LastWriteTimes.TryRemove(botName, out savedLastWriteTime)) {
|
||||
if (lastWriteTime != savedLastWriteTime) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (botName.Equals(SharedInfo.ASF)) {
|
||||
ArchiLogger.LogGenericInfo(Strings.GlobalConfigChanged);
|
||||
await RestartOrExit().ConfigureAwait(false);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsValidBotName(botName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await CreateBot(botName).ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static async void OnDeleted(object sender, FileSystemEventArgs e) {
|
||||
@@ -363,27 +450,68 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
string botName = Path.GetFileNameWithoutExtension(e.Name);
|
||||
await OnDeletedFile(e.Name, e.FullPath).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async Task<bool> OnDeletedFile(string name, string fullPath) {
|
||||
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(fullPath)) {
|
||||
ArchiLogger.LogNullError(nameof(name) + " || " + nameof(fullPath));
|
||||
return false;
|
||||
}
|
||||
|
||||
string botName = Path.GetFileNameWithoutExtension(name);
|
||||
if (string.IsNullOrEmpty(botName)) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
DateTime lastWriteTime = DateTime.UtcNow;
|
||||
|
||||
if (LastWriteTimes.TryGetValue(botName, out DateTime savedLastWriteTime)) {
|
||||
if (savedLastWriteTime >= lastWriteTime) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
LastWriteTimes[botName] = lastWriteTime;
|
||||
|
||||
// It's entirely possible that some process is still accessing our file, allow at least a second before trying to read it
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
|
||||
// It's also possible that we got some other event in the meantime
|
||||
if (LastWriteTimes.TryGetValue(botName, out savedLastWriteTime)) {
|
||||
if (lastWriteTime != savedLastWriteTime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (LastWriteTimes.TryRemove(botName, out savedLastWriteTime)) {
|
||||
if (lastWriteTime != savedLastWriteTime) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (botName.Equals(SharedInfo.ASF)) {
|
||||
if (File.Exists(fullPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Some editors might decide to delete file and re-create it in order to modify it
|
||||
// If that's the case, we wait for maximum of 5 seconds before shutting down
|
||||
await Task.Delay(5000).ConfigureAwait(false);
|
||||
if (File.Exists(e.FullPath)) {
|
||||
return;
|
||||
if (File.Exists(fullPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericError(Strings.ErrorGlobalConfigRemoved);
|
||||
await Program.Exit(1).ConfigureAwait(false);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Bot.Bots.TryGetValue(botName, out Bot bot)) {
|
||||
await bot.OnNewConfigLoaded(new BotConfigEventArgs()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static async void OnRenamed(object sender, RenamedEventArgs e) {
|
||||
@@ -392,27 +520,19 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
string oldBotName = Path.GetFileNameWithoutExtension(e.OldName);
|
||||
if (string.IsNullOrEmpty(oldBotName)) {
|
||||
return;
|
||||
// We must remember to handle all three cases here - *.any to *.json, *.json to *.any and *.json to *.json
|
||||
|
||||
string oldFileExtension = Path.GetExtension(e.OldName);
|
||||
if (!string.IsNullOrEmpty(oldFileExtension) && oldFileExtension.Equals(".json")) {
|
||||
if (!await OnDeletedFile(e.OldName, e.OldFullPath).ConfigureAwait(false)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (oldBotName.Equals(SharedInfo.ASF)) {
|
||||
ArchiLogger.LogGenericError(Strings.ErrorGlobalConfigRemoved);
|
||||
await Program.Exit(1).ConfigureAwait(false);
|
||||
return;
|
||||
string newFileExtension = Path.GetExtension(e.Name);
|
||||
if (!string.IsNullOrEmpty(newFileExtension) && newFileExtension.Equals(".json")) {
|
||||
await OnCreatedFile(e.Name, e.FullPath).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (Bot.Bots.TryGetValue(oldBotName, out Bot bot)) {
|
||||
await bot.OnNewConfigLoaded(new BotConfigEventArgs()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
string newBotName = Path.GetFileNameWithoutExtension(e.Name);
|
||||
if (string.IsNullOrEmpty(newBotName) || (newBotName[0] == '.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
await CreateBot(newBotName).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async Task RestartOrExit() {
|
||||
@@ -438,15 +558,22 @@ namespace ArchiSteamFarm {
|
||||
|
||||
// Move top-level runtime in-use files to other directory
|
||||
// We must do it in order to not crash at later stage - all libraries/executables must keep original names
|
||||
foreach (string file in Directory.GetFiles(targetDirectory)) {
|
||||
string target = Path.Combine(backupDirectory, Path.GetFileName(file));
|
||||
foreach (string file in Directory.EnumerateFiles(targetDirectory)) {
|
||||
string fileName = Path.GetFileName(file);
|
||||
switch (fileName) {
|
||||
// Files that we want to keep in original directory
|
||||
case "NLog.config":
|
||||
continue;
|
||||
}
|
||||
|
||||
string target = Path.Combine(backupDirectory, fileName);
|
||||
File.Move(file, target);
|
||||
}
|
||||
|
||||
// In generic ASF variant there can also be "runtimes" directory in need of same approach
|
||||
string runtimesDirectory = Path.Combine(targetDirectory, "runtimes");
|
||||
if (Directory.Exists(runtimesDirectory)) {
|
||||
foreach (string file in Directory.GetFiles(runtimesDirectory, "*", SearchOption.AllDirectories)) {
|
||||
foreach (string file in Directory.EnumerateFiles(runtimesDirectory, "*", SearchOption.AllDirectories)) {
|
||||
string directory = Path.Combine(backupDirectory, Path.GetDirectoryName(Path.GetRelativePath(targetDirectory, file)));
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
|
||||
@@ -96,61 +96,7 @@ namespace ArchiSteamFarm {
|
||||
Client.Send(request);
|
||||
}
|
||||
|
||||
// TODO: Remove me once https://github.com/SteamRE/SteamKit/issues/305 is fixed
|
||||
internal void LogOnWithoutMachineID(SteamUser.LogOnDetails details) {
|
||||
if (details == null) {
|
||||
throw new ArgumentNullException(nameof(details));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(details.Username) || (string.IsNullOrEmpty(details.Password) && string.IsNullOrEmpty(details.LoginKey))) {
|
||||
throw new ArgumentException("LogOn requires a username and password to be set in 'details'.");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(details.LoginKey) && !details.ShouldRememberPassword) {
|
||||
// Prevent consumers from screwing this up.
|
||||
// If should_remember_password is false, the login_key is ignored server-side.
|
||||
// The inverse is not applicable (you can log in with should_remember_password and no login_key).
|
||||
throw new ArgumentException("ShouldRememberPassword is required to be set to true in order to use LoginKey.");
|
||||
}
|
||||
|
||||
ClientMsgProtobuf<CMsgClientLogon> logon = new ClientMsgProtobuf<CMsgClientLogon>(EMsg.ClientLogon);
|
||||
|
||||
SteamID steamId = new SteamID(details.AccountID, details.AccountInstance, Client.ConnectedUniverse, EAccountType.Individual);
|
||||
|
||||
if (details.LoginID.HasValue) {
|
||||
logon.Body.obfustucated_private_ip = details.LoginID.Value;
|
||||
}
|
||||
|
||||
logon.ProtoHeader.client_sessionid = 0;
|
||||
logon.ProtoHeader.steamid = steamId.ConvertToUInt64();
|
||||
|
||||
logon.Body.account_name = details.Username;
|
||||
logon.Body.password = details.Password;
|
||||
logon.Body.should_remember_password = details.ShouldRememberPassword;
|
||||
|
||||
logon.Body.protocol_version = MsgClientLogon.CurrentProtocol;
|
||||
logon.Body.client_os_type = (uint) details.ClientOSType;
|
||||
logon.Body.client_language = details.ClientLanguage;
|
||||
logon.Body.cell_id = details.CellID;
|
||||
|
||||
logon.Body.steam2_ticket_request = details.RequestSteam2Ticket;
|
||||
|
||||
logon.Body.client_package_version = 1771;
|
||||
logon.Body.supports_rate_limit_response = true;
|
||||
|
||||
// steam guard
|
||||
logon.Body.auth_code = details.AuthCode;
|
||||
logon.Body.two_factor_code = details.TwoFactorCode;
|
||||
|
||||
logon.Body.login_key = details.LoginKey;
|
||||
|
||||
logon.Body.sha_sentryfile = details.SentryFileHash;
|
||||
logon.Body.eresult_sentryfile = (int) (details.SentryFileHash != null ? EResult.OK : EResult.FileNotFound);
|
||||
|
||||
Client.Send(logon);
|
||||
}
|
||||
|
||||
internal void PlayGames(IEnumerable<uint> gameIDs, string gameName = null) {
|
||||
internal async Task PlayGames(IEnumerable<uint> gameIDs, string gameName = null) {
|
||||
if (gameIDs == null) {
|
||||
ArchiLogger.LogNullError(nameof(gameIDs));
|
||||
return;
|
||||
@@ -163,6 +109,11 @@ namespace ArchiSteamFarm {
|
||||
ClientMsgProtobuf<CMsgClientGamesPlayed> request = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayed);
|
||||
|
||||
if (!string.IsNullOrEmpty(gameName)) {
|
||||
// If we have custom name to display, we must workaround the Steam network fuckup and send request on clean non-playing session
|
||||
// This ensures that custom name will in fact display properly
|
||||
Client.Send(request);
|
||||
await Task.Delay(Bot.CallbackSleep).ConfigureAwait(false);
|
||||
|
||||
request.Body.games_played.Add(new CMsgClientGamesPlayed.GamePlayed {
|
||||
game_extra_info = gameName,
|
||||
game_id = new GameID {
|
||||
@@ -336,6 +287,7 @@ namespace ArchiSteamFarm {
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
Unknown = 0,
|
||||
Trading = 1,
|
||||
|
||||
// Only custom below, different than ones available as user_notification_type
|
||||
Items = 254
|
||||
}
|
||||
|
||||
@@ -3,29 +3,42 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<AssemblyVersion>3.0.0.1</AssemblyVersion>
|
||||
<FileVersion>3.0.0.1</FileVersion>
|
||||
<LangVersion>7</LangVersion>
|
||||
<AssemblyVersion>3.0.2.4</AssemblyVersion>
|
||||
<FileVersion>3.0.2.4</FileVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<ErrorReport>none</ErrorReport>
|
||||
<ApplicationIcon>ASF.ico</ApplicationIcon>
|
||||
<Copyright>Copyright © ArchiSteamFarm 2015-2017</Copyright>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;linux-arm;osx-x64</RuntimeIdentifiers>
|
||||
<Description>ASF is an application that allows you to farm steam cards using multiple steam accounts simultaneously.</Description>
|
||||
<Authors>JustArchi</Authors>
|
||||
<Company>JustArchi</Company>
|
||||
<PackageLicenseUrl>http://www.apache.org/licenses/LICENSE-2.0</PackageLicenseUrl>
|
||||
<PackageProjectUrl>https://github.com/JustArchi/ArchiSteamFarm</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/JustArchi/ArchiSteamFarm.git</RepositoryUrl>
|
||||
<PackageIconUrl>https://github.com/JustArchi/ArchiSteamFarm/raw/master/resources/ASF.ico</PackageIconUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<NoWarn />
|
||||
<ServerGarbageCollection>false</ServerGarbageCollection>
|
||||
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DebugType>none</DebugType>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.5.0" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.5.5" />
|
||||
<PackageReference Include="Humanizer" Version="2.2.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
<PackageReference Include="Nito.AsyncEx.Coordination" Version="5.0.0-pre-02" />
|
||||
<PackageReference Include="NLog" Version="5.0.0-beta09" />
|
||||
<PackageReference Include="SteamKit2" Version="2.0.0-Alpha4" />
|
||||
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="4.4.0-preview2-25405-01" />
|
||||
<PackageReference Include="NLog" Version="5.0.0-beta10" />
|
||||
<PackageReference Include="SteamKit2" Version="2.0.0-Alpha8" />
|
||||
<PackageReference Include="System.Net.NameResolution" Version="4.3.0" />
|
||||
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="4.4.0" />
|
||||
<PackageReference Include="System.Threading.ThreadPool" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -44,6 +57,9 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="ArchiSteamFarm.version">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="config\ASF.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
||||
1
ArchiSteamFarm/ArchiSteamFarm.version
Normal file
1
ArchiSteamFarm/ArchiSteamFarm.version
Normal file
@@ -0,0 +1 @@
|
||||
source
|
||||
@@ -50,21 +50,21 @@ namespace ArchiSteamFarm {
|
||||
|
||||
// We must use HTTPS for SteamCommunity, as http would make certain POST requests failing (trades)
|
||||
private const string SteamCommunityHost = "steamcommunity.com";
|
||||
|
||||
private const string SteamCommunityURL = "https://" + SteamCommunityHost;
|
||||
|
||||
// We could (and should) use HTTPS for SteamStore, but that would make certain POST requests failing
|
||||
private const string SteamStoreHost = "store.steampowered.com";
|
||||
|
||||
private const string SteamStoreURL = "http://" + SteamStoreHost;
|
||||
|
||||
private static readonly SemaphoreSlim InventorySemaphore = new SemaphoreSlim(1);
|
||||
private static readonly SemaphoreSlim InventorySemaphore = new SemaphoreSlim(1, 1);
|
||||
|
||||
private static int Timeout = GlobalConfig.DefaultConnectionTimeout * 1000; // This must be int type
|
||||
|
||||
private readonly SemaphoreSlim ApiKeySemaphore = new SemaphoreSlim(1);
|
||||
private readonly SemaphoreSlim ApiKeySemaphore = new SemaphoreSlim(1, 1);
|
||||
private readonly Bot Bot;
|
||||
private readonly SemaphoreSlim PublicInventorySemaphore = new SemaphoreSlim(1);
|
||||
private readonly SemaphoreSlim SessionSemaphore = new SemaphoreSlim(1);
|
||||
private readonly SemaphoreSlim TradeTokenSemaphore = new SemaphoreSlim(1);
|
||||
private readonly SemaphoreSlim PublicInventorySemaphore = new SemaphoreSlim(1, 1);
|
||||
private readonly SemaphoreSlim SessionSemaphore = new SemaphoreSlim(1, 1);
|
||||
private readonly SemaphoreSlim TradeTokenSemaphore = new SemaphoreSlim(1, 1);
|
||||
private readonly WebBrowser WebBrowser;
|
||||
|
||||
private string CachedApiKey;
|
||||
@@ -72,6 +72,7 @@ namespace ArchiSteamFarm {
|
||||
private string CachedTradeToken;
|
||||
private DateTime LastSessionRefreshCheck = DateTime.MinValue;
|
||||
private ulong SteamID;
|
||||
private string VanityURL;
|
||||
|
||||
internal ArchiWebHandler(Bot bot) {
|
||||
Bot = bot ?? throw new ArgumentNullException(nameof(bot));
|
||||
@@ -83,6 +84,7 @@ namespace ArchiSteamFarm {
|
||||
PublicInventorySemaphore.Dispose();
|
||||
SessionSemaphore.Dispose();
|
||||
TradeTokenSemaphore.Dispose();
|
||||
WebBrowser.Dispose();
|
||||
}
|
||||
|
||||
internal async Task<bool> AcceptTradeOffer(ulong tradeID) {
|
||||
@@ -177,26 +179,24 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
KeyValue response = null;
|
||||
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
|
||||
await Task.Run(() => {
|
||||
using (dynamic iEconService = WebAPI.GetInterface(IEconService, steamApiKey)) {
|
||||
iEconService.Timeout = Timeout;
|
||||
for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) {
|
||||
using (dynamic iEconService = WebAPI.GetAsyncInterface(IEconService, steamApiKey)) {
|
||||
iEconService.Timeout = WebBrowser.Timeout;
|
||||
|
||||
try {
|
||||
response = iEconService.DeclineTradeOffer(
|
||||
tradeofferid: tradeID.ToString(),
|
||||
method: WebRequestMethods.Http.Post,
|
||||
secure: true
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Bot.ArchiLogger.LogGenericWarningException(e);
|
||||
}
|
||||
try {
|
||||
response = await iEconService.DeclineTradeOffer(
|
||||
tradeofferid: tradeID.ToString(),
|
||||
method: WebRequestMethods.Http.Post,
|
||||
secure: true
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Bot.ArchiLogger.LogGenericWarningException(e);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxRetries));
|
||||
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,28 +228,26 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
KeyValue response = null;
|
||||
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
|
||||
await Task.Run(() => {
|
||||
using (dynamic iEconService = WebAPI.GetInterface(IEconService, steamApiKey)) {
|
||||
iEconService.Timeout = Timeout;
|
||||
for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) {
|
||||
using (dynamic iEconService = WebAPI.GetAsyncInterface(IEconService, steamApiKey)) {
|
||||
iEconService.Timeout = WebBrowser.Timeout;
|
||||
|
||||
try {
|
||||
response = iEconService.GetTradeOffers(
|
||||
active_only: 1,
|
||||
get_descriptions: 1,
|
||||
get_received_offers: 1,
|
||||
secure: true,
|
||||
time_historical_cutoff: uint.MaxValue
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Bot.ArchiLogger.LogGenericWarningException(e);
|
||||
}
|
||||
try {
|
||||
response = await iEconService.GetTradeOffers(
|
||||
active_only: 1,
|
||||
get_descriptions: 1,
|
||||
get_received_offers: 1,
|
||||
secure: true,
|
||||
time_historical_cutoff: uint.MaxValue
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Bot.ArchiLogger.LogGenericWarningException(e);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxRetries));
|
||||
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -479,7 +477,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "FunctionComplexityOverflow")]
|
||||
internal async Task<HashSet<Steam.Item>> GetMySteamInventory(bool tradable, HashSet<Steam.Item.EType> wantedTypes, HashSet<uint> wantedRealAppIDs = null) {
|
||||
internal async Task<HashSet<Steam.Item>> GetMySteamInventory(bool trading, HashSet<Steam.Item.EType> wantedTypes, HashSet<uint> wantedRealAppIDs = null) {
|
||||
if ((wantedTypes == null) || (wantedTypes.Count == 0)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(wantedTypes));
|
||||
return null;
|
||||
@@ -491,7 +489,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
HashSet<Steam.Item> result = new HashSet<Steam.Item>();
|
||||
|
||||
string request = SteamCommunityURL + "/my/inventory/json/" + Steam.Item.SteamAppID + "/" + Steam.Item.SteamCommunityContextID + "?l=english&trading=" + (tradable ? "1" : "0") + "&start=";
|
||||
string request = SteamCommunityURL + "/my/inventory/json/" + Steam.Item.SteamAppID + "/" + Steam.Item.SteamCommunityContextID + "?l=english" + (trading ? "&trading=1" : "") + "&start=";
|
||||
uint currentPage = 0;
|
||||
|
||||
await InventorySemaphore.WaitAsync().ConfigureAwait(false);
|
||||
@@ -625,26 +623,24 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
KeyValue response = null;
|
||||
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
|
||||
await Task.Run(() => {
|
||||
using (dynamic iPlayerService = WebAPI.GetInterface(IPlayerService, steamApiKey)) {
|
||||
iPlayerService.Timeout = Timeout;
|
||||
for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) {
|
||||
using (dynamic iPlayerService = WebAPI.GetAsyncInterface(IPlayerService, steamApiKey)) {
|
||||
iPlayerService.Timeout = WebBrowser.Timeout;
|
||||
|
||||
try {
|
||||
response = iPlayerService.GetOwnedGames(
|
||||
steamid: steamID,
|
||||
include_appinfo: 1,
|
||||
secure: true
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Bot.ArchiLogger.LogGenericWarningException(e);
|
||||
}
|
||||
try {
|
||||
response = await iPlayerService.GetOwnedGames(
|
||||
steamid: steamID,
|
||||
include_appinfo: 1,
|
||||
secure: true
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Bot.ArchiLogger.LogGenericWarningException(e);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxRetries));
|
||||
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -664,28 +660,26 @@ namespace ArchiSteamFarm {
|
||||
|
||||
internal async Task<uint> GetServerTime() {
|
||||
KeyValue response = null;
|
||||
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
|
||||
await Task.Run(() => {
|
||||
using (dynamic iTwoFactorService = WebAPI.GetInterface(ITwoFactorService)) {
|
||||
iTwoFactorService.Timeout = Timeout;
|
||||
for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) {
|
||||
using (dynamic iTwoFactorService = WebAPI.GetAsyncInterface(ITwoFactorService)) {
|
||||
iTwoFactorService.Timeout = WebBrowser.Timeout;
|
||||
|
||||
try {
|
||||
response = iTwoFactorService.QueryTime(
|
||||
method: WebRequestMethods.Http.Post,
|
||||
secure: true
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Bot.ArchiLogger.LogGenericWarningException(e);
|
||||
}
|
||||
try {
|
||||
response = await iTwoFactorService.QueryTime(
|
||||
method: WebRequestMethods.Http.Post,
|
||||
secure: true
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Bot.ArchiLogger.LogGenericWarningException(e);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (response != null) {
|
||||
return response["server_time"].AsUnsignedInteger();
|
||||
}
|
||||
|
||||
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxRetries));
|
||||
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -863,14 +857,16 @@ namespace ArchiSteamFarm {
|
||||
|
||||
internal async Task<bool> HasValidApiKey() => !string.IsNullOrEmpty(await GetApiKey().ConfigureAwait(false));
|
||||
|
||||
internal static void Init() => Timeout = Program.GlobalConfig.ConnectionTimeout * 1000;
|
||||
|
||||
internal async Task<bool> Init(ulong steamID, EUniverse universe, string webAPIUserNonce, string parentalPin) {
|
||||
internal async Task<bool> Init(ulong steamID, EUniverse universe, string webAPIUserNonce, string parentalPin, string vanityURL = null) {
|
||||
if ((steamID == 0) || (universe == EUniverse.Invalid) || string.IsNullOrEmpty(webAPIUserNonce) || string.IsNullOrEmpty(parentalPin)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(universe) + " || " + nameof(webAPIUserNonce) + " || " + nameof(parentalPin));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(vanityURL)) {
|
||||
VanityURL = vanityURL;
|
||||
}
|
||||
|
||||
string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(steamID.ToString()));
|
||||
|
||||
// Generate an AES session key
|
||||
@@ -893,23 +889,21 @@ namespace ArchiSteamFarm {
|
||||
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.LoggingIn, ISteamUserAuth));
|
||||
|
||||
KeyValue authResult = null;
|
||||
await Task.Run(() => {
|
||||
using (dynamic iSteamUserAuth = WebAPI.GetInterface(ISteamUserAuth)) {
|
||||
iSteamUserAuth.Timeout = Timeout;
|
||||
using (dynamic iSteamUserAuth = WebAPI.GetAsyncInterface(ISteamUserAuth)) {
|
||||
iSteamUserAuth.Timeout = WebBrowser.Timeout;
|
||||
|
||||
try {
|
||||
authResult = iSteamUserAuth.AuthenticateUser(
|
||||
steamid: steamID,
|
||||
sessionkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedSessionKey, 0, cryptedSessionKey.Length)),
|
||||
encrypted_loginkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedLoginKey, 0, cryptedLoginKey.Length)),
|
||||
method: WebRequestMethods.Http.Post,
|
||||
secure: true
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Bot.ArchiLogger.LogGenericWarningException(e);
|
||||
}
|
||||
try {
|
||||
authResult = await iSteamUserAuth.AuthenticateUser(
|
||||
steamid: steamID,
|
||||
sessionkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedSessionKey, 0, cryptedSessionKey.Length)),
|
||||
encrypted_loginkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedLoginKey, 0, cryptedLoginKey.Length)),
|
||||
method: WebRequestMethods.Http.Post,
|
||||
secure: true
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Bot.ArchiLogger.LogGenericWarningException(e);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (authResult == null) {
|
||||
return false;
|
||||
@@ -1086,6 +1080,41 @@ namespace ArchiSteamFarm {
|
||||
return true;
|
||||
}
|
||||
|
||||
internal async Task<bool> UnpackBooster(uint appID, ulong itemID) {
|
||||
if ((appID == 0) || (itemID == 0)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(appID) + " || " + nameof(itemID));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
|
||||
if (string.IsNullOrEmpty(sessionID)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(sessionID));
|
||||
return false;
|
||||
}
|
||||
|
||||
string request = GetAbsoluteProfileURL() + "/ajaxunpackbooster";
|
||||
Dictionary<string, string> data = new Dictionary<string, string>(3) {
|
||||
{ "sessionid", sessionID },
|
||||
{ "appid", appID.ToString() },
|
||||
{ "communityitemid", itemID.ToString() }
|
||||
};
|
||||
|
||||
Steam.GenericResponse response = await WebBrowser.UrlPostToJsonResultRetry<Steam.GenericResponse>(request, data).ConfigureAwait(false);
|
||||
return response?.Result == EResult.OK;
|
||||
}
|
||||
|
||||
private string GetAbsoluteProfileURL() {
|
||||
if (!string.IsNullOrEmpty(VanityURL)) {
|
||||
return SteamCommunityURL + "/id/" + VanityURL;
|
||||
}
|
||||
|
||||
return SteamCommunityURL + "/profiles/" + SteamID;
|
||||
}
|
||||
|
||||
private async Task<string> GetApiKey() {
|
||||
if (CachedApiKey != null) {
|
||||
// We fetched API key already, and either got valid one, or permanent AccessDenied
|
||||
@@ -1136,7 +1165,7 @@ namespace ArchiSteamFarm {
|
||||
break;
|
||||
default:
|
||||
// We got an unhandled error, this should never happen
|
||||
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(result.Value.State), result.Value.State));
|
||||
Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(result.Value.State), result.Value.State));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,23 +22,6 @@
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class IPAddressConverter : JsonConverter {
|
||||
public override bool CanConvert(Type objectType) => objectType == typeof(IPAddress);
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
|
||||
JToken token = JToken.Load(reader);
|
||||
return IPAddress.Parse(token.Value<string>());
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
|
||||
IPAddress ip = (IPAddress) value;
|
||||
writer.WriteValue(ip.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
[assembly: InternalsVisibleTo("ArchiSteamFarm.Tests")]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -41,6 +41,11 @@ namespace ArchiSteamFarm {
|
||||
internal readonly bool AcceptGifts;
|
||||
#pragma warning restore 649
|
||||
|
||||
#pragma warning disable 649
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly bool AutoDiscoveryQueue;
|
||||
#pragma warning restore 649
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly bool CardDropsRestricted = true;
|
||||
|
||||
@@ -80,6 +85,11 @@ namespace ArchiSteamFarm {
|
||||
internal readonly bool HandleOfflineMessages;
|
||||
#pragma warning restore 649
|
||||
|
||||
#pragma warning disable 649
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly bool IdleRefundableGames = true;
|
||||
#pragma warning restore 649
|
||||
|
||||
#pragma warning disable 649
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly bool IsBotAccount;
|
||||
@@ -92,6 +102,11 @@ namespace ArchiSteamFarm {
|
||||
Steam.Item.EType.TradingCard
|
||||
};
|
||||
|
||||
[JsonProperty(ObjectCreationHandling = ObjectCreationHandling.Replace, Required = Required.DisallowNull)]
|
||||
internal readonly HashSet<Steam.Item.EType> MatchableTypes = new HashSet<Steam.Item.EType> {
|
||||
Steam.Item.EType.TradingCard
|
||||
};
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly CryptoHelper.ECryptoMethod PasswordFormat = CryptoHelper.ECryptoMethod.PlainText;
|
||||
|
||||
@@ -157,6 +172,11 @@ namespace ArchiSteamFarm {
|
||||
// This constructor is used only by deserializer
|
||||
private BotConfig() { }
|
||||
|
||||
// Functions below are used for skipping serialization of sensitive fields in API response
|
||||
public bool ShouldSerializeSteamLogin() => false;
|
||||
public bool ShouldSerializeSteamParentalPIN() => false;
|
||||
public bool ShouldSerializeSteamPassword() => false;
|
||||
|
||||
internal static BotConfig Load(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(filePath));
|
||||
@@ -212,7 +232,11 @@ namespace ArchiSteamFarm {
|
||||
HoursDescending,
|
||||
NamesAscending,
|
||||
NamesDescending,
|
||||
Random
|
||||
Random,
|
||||
BadgeLevelsAscending,
|
||||
BadgeLevelsDescending,
|
||||
RedeemDateTimesAscending,
|
||||
RedeemDateTimesDescending
|
||||
}
|
||||
|
||||
internal enum EPermission : byte {
|
||||
|
||||
@@ -27,46 +27,24 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class BotDatabase {
|
||||
internal sealed class BotDatabase : IDisposable {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
private readonly ConcurrentHashSet<ulong> BlacklistedFromTradesSteamIDs = new ConcurrentHashSet<ulong>();
|
||||
|
||||
private readonly object FileLock = new object();
|
||||
private readonly SemaphoreSlim FileSemaphore = new SemaphoreSlim(1, 1);
|
||||
|
||||
internal string LoginKey {
|
||||
get => _LoginKey;
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
private readonly ConcurrentHashSet<uint> IdlingPriorityAppIDs = new ConcurrentHashSet<uint>();
|
||||
|
||||
set {
|
||||
if (_LoginKey == value) {
|
||||
return;
|
||||
}
|
||||
[JsonProperty(PropertyName = "_LoginKey")]
|
||||
internal string LoginKey { get; private set; }
|
||||
|
||||
_LoginKey = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
internal MobileAuthenticator MobileAuthenticator {
|
||||
get => _MobileAuthenticator;
|
||||
|
||||
set {
|
||||
if (_MobileAuthenticator == value) {
|
||||
return;
|
||||
}
|
||||
|
||||
_MobileAuthenticator = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty]
|
||||
private string _LoginKey;
|
||||
|
||||
[JsonProperty]
|
||||
private MobileAuthenticator _MobileAuthenticator;
|
||||
[JsonProperty(PropertyName = "_MobileAuthenticator")]
|
||||
internal MobileAuthenticator MobileAuthenticator { get; private set; }
|
||||
|
||||
private string FilePath;
|
||||
|
||||
@@ -77,33 +55,75 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
FilePath = filePath;
|
||||
Save();
|
||||
Save().Wait();
|
||||
}
|
||||
|
||||
// This constructor is used only by deserializer
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||
private BotDatabase() { }
|
||||
|
||||
internal void AddBlacklistedFromTradesSteamIDs(HashSet<ulong> steamIDs) {
|
||||
public void Dispose() {
|
||||
// Those are objects that are always being created if constructor doesn't throw exception
|
||||
FileSemaphore.Dispose();
|
||||
|
||||
// Those are objects that might be null and the check should be in-place
|
||||
MobileAuthenticator?.Dispose();
|
||||
}
|
||||
|
||||
internal async Task AddBlacklistedFromTradesSteamIDs(HashSet<ulong> steamIDs) {
|
||||
if ((steamIDs == null) || (steamIDs.Count == 0)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamIDs));
|
||||
return;
|
||||
}
|
||||
|
||||
if (BlacklistedFromTradesSteamIDs.AddRange(steamIDs)) {
|
||||
Save();
|
||||
await Save().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task AddIdlingPriorityAppIDs(HashSet<uint> appIDs) {
|
||||
if ((appIDs == null) || (appIDs.Count == 0)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(appIDs));
|
||||
return;
|
||||
}
|
||||
|
||||
if (IdlingPriorityAppIDs.AddRange(appIDs)) {
|
||||
await Save().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task CorrectMobileAuthenticatorDeviceID(string deviceID) {
|
||||
if (string.IsNullOrEmpty(deviceID) || (MobileAuthenticator == null)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(deviceID) + " || " + nameof(MobileAuthenticator));
|
||||
return;
|
||||
}
|
||||
|
||||
if (MobileAuthenticator.CorrectDeviceID(deviceID)) {
|
||||
await Save().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<ulong> GetBlacklistedFromTradesSteamIDs() => BlacklistedFromTradesSteamIDs;
|
||||
internal IEnumerable<uint> GetIdlingPriorityAppIDs() => IdlingPriorityAppIDs;
|
||||
|
||||
internal bool IsBlacklistedFromTrades(ulong steamID) {
|
||||
if (steamID != 0) {
|
||||
return BlacklistedFromTradesSteamIDs.Contains(steamID);
|
||||
if (steamID == 0) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID));
|
||||
return false;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamID));
|
||||
return false;
|
||||
bool result = BlacklistedFromTradesSteamIDs.Contains(steamID);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal bool IsPriorityIdling(uint appID) {
|
||||
if (appID == 0) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(appID));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = IdlingPriorityAppIDs.Contains(appID);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static BotDatabase Load(string filePath) {
|
||||
@@ -134,40 +154,69 @@ namespace ArchiSteamFarm {
|
||||
return botDatabase;
|
||||
}
|
||||
|
||||
internal void RemoveBlacklistedFromTradesSteamIDs(HashSet<ulong> steamIDs) {
|
||||
internal async Task RemoveBlacklistedFromTradesSteamIDs(HashSet<ulong> steamIDs) {
|
||||
if ((steamIDs == null) || (steamIDs.Count == 0)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamIDs));
|
||||
return;
|
||||
}
|
||||
|
||||
if (BlacklistedFromTradesSteamIDs.RemoveRange(steamIDs)) {
|
||||
Save();
|
||||
await Save().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
internal void Save() {
|
||||
internal async Task RemoveIdlingPriorityAppIDs(HashSet<uint> appIDs) {
|
||||
if ((appIDs == null) || (appIDs.Count == 0)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(appIDs));
|
||||
return;
|
||||
}
|
||||
|
||||
if (IdlingPriorityAppIDs.RemoveRange(appIDs)) {
|
||||
await Save().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task SetLoginKey(string value = null) {
|
||||
if (value == LoginKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
LoginKey = value;
|
||||
await Save().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal async Task SetMobileAuthenticator(MobileAuthenticator value = null) {
|
||||
if (value == MobileAuthenticator) {
|
||||
return;
|
||||
}
|
||||
|
||||
MobileAuthenticator = value;
|
||||
await Save().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task Save() {
|
||||
string json = JsonConvert.SerializeObject(this);
|
||||
if (string.IsNullOrEmpty(json)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(json));
|
||||
return;
|
||||
}
|
||||
|
||||
// This call verifies if JSON is alright
|
||||
// We don't wrap it in try catch as it should always be the case
|
||||
// And if it's not, we want to know about it (in a crash) and correct it in future version
|
||||
JsonConvert.DeserializeObject<BotDatabase>(json);
|
||||
string newFilePath = FilePath + ".new";
|
||||
|
||||
lock (FileLock) {
|
||||
for (byte i = 0; i < 5; i++) {
|
||||
try {
|
||||
File.WriteAllText(FilePath, json);
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
}
|
||||
await FileSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
Thread.Sleep(1000);
|
||||
try {
|
||||
await File.WriteAllTextAsync(newFilePath, json).ConfigureAwait(false);
|
||||
|
||||
if (File.Exists(FilePath)) {
|
||||
File.Replace(newFilePath, FilePath, null);
|
||||
} else {
|
||||
File.Move(newFilePath, FilePath);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
} finally {
|
||||
FileSemaphore.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,10 +34,13 @@ using System.Threading.Tasks;
|
||||
using ArchiSteamFarm.Localization;
|
||||
using HtmlAgilityPack;
|
||||
using Newtonsoft.Json;
|
||||
using SteamKit2;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class CardsFarmer : IDisposable {
|
||||
private const byte HoursToBump = 2; // How many hours are required for restricted accounts
|
||||
internal const byte DaysForRefund = 14; // In how many days since payment we're allowed to refund
|
||||
internal const byte HoursToBump = 2; // How many hours are required for restricted accounts
|
||||
|
||||
private const byte HoursToIgnore = 24; // How many hours we ignore unreleased appIDs and don't bother checking them again
|
||||
|
||||
private static readonly ConcurrentDictionary<uint, DateTime> IgnoredAppIDs = new ConcurrentDictionary<uint, DateTime>(); // Reserved for unreleased games
|
||||
@@ -47,27 +50,29 @@ namespace ArchiSteamFarm {
|
||||
internal readonly ConcurrentHashSet<Game> CurrentGamesFarming = new ConcurrentHashSet<Game>();
|
||||
|
||||
[JsonProperty]
|
||||
internal readonly ConcurrentHashSet<Game> GamesToFarm = new ConcurrentHashSet<Game>();
|
||||
internal readonly ConcurrentSortedHashSet<Game> GamesToFarm = new ConcurrentSortedHashSet<Game>();
|
||||
|
||||
[JsonProperty]
|
||||
internal TimeSpan TimeRemaining => new TimeSpan(
|
||||
Bot.BotConfig.CardDropsRestricted ? (int) Math.Ceiling(GamesToFarm.Count / (float) ArchiHandler.MaxGamesPlayedConcurrently) * HoursToBump : 0,
|
||||
Bot.BotConfig.CardDropsRestricted ? (ushort) Math.Ceiling(GamesToFarm.Count / (float) ArchiHandler.MaxGamesPlayedConcurrently) * HoursToBump : 0,
|
||||
30 * GamesToFarm.Sum(game => game.CardsRemaining),
|
||||
0
|
||||
);
|
||||
|
||||
private readonly Bot Bot;
|
||||
private readonly SemaphoreSlim EventSemaphore = new SemaphoreSlim(1);
|
||||
private readonly SemaphoreSlim FarmingSemaphore = new SemaphoreSlim(1);
|
||||
private readonly ManualResetEventSlim FarmResetEvent = new ManualResetEventSlim(false);
|
||||
private readonly SemaphoreSlim EventSemaphore = new SemaphoreSlim(1, 1);
|
||||
private readonly SemaphoreSlim FarmingInitializationSemaphore = new SemaphoreSlim(1, 1);
|
||||
private readonly SemaphoreSlim FarmingResetSemaphore = new SemaphoreSlim(0, 1);
|
||||
private readonly Timer IdleFarmingTimer;
|
||||
|
||||
internal bool NowFarming { get; private set; }
|
||||
|
||||
[JsonProperty]
|
||||
internal bool Paused { get; private set; }
|
||||
|
||||
private bool KeepFarming;
|
||||
private bool NowFarming;
|
||||
private bool ParsingScheduled;
|
||||
private bool ShouldResumeFarming = true;
|
||||
private bool StickyPause;
|
||||
|
||||
internal CardsFarmer(Bot bot) {
|
||||
@@ -86,8 +91,9 @@ namespace ArchiSteamFarm {
|
||||
public void Dispose() {
|
||||
// Those are objects that are always being created if constructor doesn't throw exception
|
||||
EventSemaphore.Dispose();
|
||||
FarmingSemaphore.Dispose();
|
||||
FarmResetEvent.Dispose();
|
||||
FarmingInitializationSemaphore.Dispose();
|
||||
FarmingResetSemaphore.Dispose();
|
||||
GamesToFarm.Dispose();
|
||||
|
||||
// Those are objects that might be null and the check should be in-place
|
||||
IdleFarmingTimer?.Dispose();
|
||||
@@ -102,6 +108,8 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
internal async Task OnNewGameAdded() {
|
||||
ShouldResumeFarming = true;
|
||||
|
||||
// We aim to have a maximum of 2 tasks, one already parsing, and one waiting in the queue
|
||||
// This way we can call this function as many times as needed e.g. because of Steam events
|
||||
lock (EventSemaphore) {
|
||||
@@ -139,8 +147,19 @@ namespace ArchiSteamFarm {
|
||||
|
||||
internal async Task OnNewItemsNotification() {
|
||||
if (NowFarming) {
|
||||
FarmResetEvent.Set();
|
||||
return;
|
||||
await FarmingInitializationSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
try {
|
||||
if (NowFarming) {
|
||||
if (FarmingResetSemaphore.CurrentCount == 0) {
|
||||
FarmingResetSemaphore.Release();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
FarmingInitializationSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
// If we're not farming, and we got new items, it's likely to be a booster pack or likewise
|
||||
@@ -154,25 +173,36 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
Paused = true;
|
||||
if (NowFarming) {
|
||||
await StopFarming().ConfigureAwait(false);
|
||||
|
||||
if (!NowFarming) {
|
||||
return;
|
||||
}
|
||||
|
||||
await StopFarming().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal async Task Resume(bool userAction) {
|
||||
internal async Task<bool> Resume(bool userAction) {
|
||||
if (StickyPause) {
|
||||
if (!userAction) {
|
||||
Bot.ArchiLogger.LogGenericInfo(Strings.IgnoredStickyPauseEnabled);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
StickyPause = false;
|
||||
}
|
||||
|
||||
Paused = false;
|
||||
if (!NowFarming) {
|
||||
await StartFarming().ConfigureAwait(false);
|
||||
|
||||
if (NowFarming) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!userAction && !ShouldResumeFarming) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await StartFarming().ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void SetInitialState(bool paused) => StickyPause = Paused = paused;
|
||||
@@ -187,7 +217,7 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
await FarmingSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
await FarmingInitializationSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
try {
|
||||
if (NowFarming || Paused || !Bot.IsPlayingPossible) {
|
||||
@@ -230,7 +260,7 @@ namespace ArchiSteamFarm {
|
||||
KeepFarming = NowFarming = true;
|
||||
Utilities.StartBackgroundFunction(Farm);
|
||||
} finally {
|
||||
FarmingSemaphore.Release();
|
||||
FarmingInitializationSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +269,7 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
await FarmingSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
await FarmingInitializationSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
try {
|
||||
if (!NowFarming) {
|
||||
@@ -247,9 +277,12 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
KeepFarming = false;
|
||||
FarmResetEvent.Set();
|
||||
|
||||
for (byte i = 0; (i < 5) && NowFarming; i++) {
|
||||
if (FarmingResetSemaphore.CurrentCount == 0) {
|
||||
FarmingResetSemaphore.Release();
|
||||
}
|
||||
|
||||
for (byte i = 0; (i < WebBrowser.MaxTries) && NowFarming; i++) {
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -258,13 +291,13 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
Bot.ArchiLogger.LogGenericInfo(Strings.IdlingStopped);
|
||||
Bot.OnFarmingStopped();
|
||||
await Bot.OnFarmingStopped().ConfigureAwait(false);
|
||||
} finally {
|
||||
FarmingSemaphore.Release();
|
||||
FarmingInitializationSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CheckGame(uint appID, string name, float hours) {
|
||||
private async Task CheckGame(uint appID, string name, float hours, byte badgeLevel) {
|
||||
if ((appID == 0) || string.IsNullOrEmpty(name) || (hours < 0)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(appID) + " || " + nameof(name) + " || " + nameof(hours));
|
||||
return;
|
||||
@@ -280,7 +313,7 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
GamesToFarm.Add(new Game(appID, name, hours, cardsRemaining.Value));
|
||||
GamesToFarm.Add(new Game(appID, name, hours, cardsRemaining.Value, badgeLevel));
|
||||
}
|
||||
|
||||
private async Task CheckGamesForFarming() {
|
||||
@@ -297,7 +330,7 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
HtmlNodeCollection htmlNodes = htmlDocument.DocumentNode.SelectNodes("//div[@class='badge_title_stats_content']");
|
||||
HtmlNodeCollection htmlNodes = htmlDocument.DocumentNode.SelectNodes("//div[@class='badge_row_inner']");
|
||||
if (htmlNodes == null) {
|
||||
// No eligible badges whatsoever
|
||||
return;
|
||||
@@ -306,7 +339,9 @@ namespace ArchiSteamFarm {
|
||||
HashSet<Task> backgroundTasks = new HashSet<Task>();
|
||||
|
||||
foreach (HtmlNode htmlNode in htmlNodes) {
|
||||
HtmlNode appIDNode = htmlNode.SelectSingleNode(".//div[@class='card_drop_info_dialog']");
|
||||
HtmlNode statsNode = htmlNode.SelectSingleNode(".//div[@class='badge_title_stats_content']");
|
||||
|
||||
HtmlNode appIDNode = statsNode?.SelectSingleNode(".//div[@class='card_drop_info_dialog']");
|
||||
if (appIDNode == null) {
|
||||
// It's just a badge, nothing more
|
||||
continue;
|
||||
@@ -331,15 +366,15 @@ namespace ArchiSteamFarm {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (GlobalConfig.GlobalBlacklist.Contains(appID) || Program.GlobalConfig.Blacklist.Contains(appID)) {
|
||||
if (GlobalConfig.GamesBlacklist.Contains(appID) || GlobalConfig.SalesBlacklist.Contains(appID) || Program.GlobalConfig.Blacklist.Contains(appID)) {
|
||||
// We have this appID blacklisted, so skip it
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IgnoredAppIDs.TryGetValue(appID, out DateTime lastPICSReport)) {
|
||||
if (lastPICSReport.AddHours(HoursToIgnore) < DateTime.UtcNow) {
|
||||
if (IgnoredAppIDs.TryGetValue(appID, out DateTime ignoredUntil)) {
|
||||
if (ignoredUntil < DateTime.UtcNow) {
|
||||
// This game served its time as being ignored
|
||||
IgnoredAppIDs.TryRemove(appID, out lastPICSReport);
|
||||
IgnoredAppIDs.TryRemove(appID, out _);
|
||||
} else {
|
||||
// This game is still ignored
|
||||
continue;
|
||||
@@ -347,7 +382,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
// Cards
|
||||
HtmlNode progressNode = htmlNode.SelectSingleNode(".//span[@class='progress_info_bold']");
|
||||
HtmlNode progressNode = statsNode.SelectSingleNode(".//span[@class='progress_info_bold']");
|
||||
if (progressNode == null) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(progressNode));
|
||||
continue;
|
||||
@@ -380,7 +415,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
// To save us on extra work, check cards earned so far first
|
||||
HtmlNode cardsEarnedNode = htmlNode.SelectSingleNode(".//div[@class='card_drop_info_header']");
|
||||
HtmlNode cardsEarnedNode = statsNode.SelectSingleNode(".//div[@class='card_drop_info_header']");
|
||||
if (cardsEarnedNode == null) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(cardsEarnedNode));
|
||||
continue;
|
||||
@@ -419,7 +454,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
// Hours
|
||||
HtmlNode timeNode = htmlNode.SelectSingleNode(".//div[@class='badge_title_stats_playtime']");
|
||||
HtmlNode timeNode = statsNode.SelectSingleNode(".//div[@class='badge_title_stats_playtime']");
|
||||
if (timeNode == null) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(timeNode));
|
||||
continue;
|
||||
@@ -443,7 +478,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
// Names
|
||||
HtmlNode nameNode = htmlNode.SelectSingleNode("(.//div[@class='card_drop_info_body'])[last()]");
|
||||
HtmlNode nameNode = statsNode.SelectSingleNode("(.//div[@class='card_drop_info_body'])[last()]");
|
||||
if (nameNode == null) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(nameNode));
|
||||
continue;
|
||||
@@ -477,14 +512,44 @@ namespace ArchiSteamFarm {
|
||||
|
||||
name = WebUtility.HtmlDecode(name.Substring(nameStartIndex, nameEndIndex - nameStartIndex));
|
||||
|
||||
// We have two possible cases here
|
||||
// Either we have decent info about appID, name, hours and cardsRemaining (cardsRemaining > 0)
|
||||
// OR we strongly believe that Steam lied to us, in this case we will need to check game invidually (cardsRemaining == 0)
|
||||
// Levels
|
||||
byte badgeLevel = 0;
|
||||
|
||||
HtmlNode levelNode = htmlNode.SelectSingleNode(".//div[@class='badge_info_description']/div[2]");
|
||||
if (levelNode != null) {
|
||||
// There is no levelNode if we didn't craft that badge yet (level 0)
|
||||
string levelString = levelNode.InnerText;
|
||||
if (string.IsNullOrEmpty(levelString)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(levelString));
|
||||
continue;
|
||||
}
|
||||
|
||||
int levelIndex = levelString.IndexOf("Level ", StringComparison.OrdinalIgnoreCase);
|
||||
if (levelIndex < 0) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(levelIndex));
|
||||
continue;
|
||||
}
|
||||
|
||||
levelIndex += 6;
|
||||
if (levelString.Length <= levelIndex) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(levelIndex));
|
||||
continue;
|
||||
}
|
||||
|
||||
levelString = levelString.Substring(levelIndex, 1);
|
||||
if (!byte.TryParse(levelString, out badgeLevel) || (badgeLevel == 0) || (badgeLevel > 5)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(badgeLevel));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Done with parsing, we have two possible cases here
|
||||
// Either we have decent info about appID, name, hours, cardsRemaining (cardsRemaining > 0) and level
|
||||
// OR we strongly believe that Steam lied to us, in this case we will need to check game invidually (cardsRemaining == 0)
|
||||
if (cardsRemaining > 0) {
|
||||
GamesToFarm.Add(new Game(appID, name, hours, cardsRemaining));
|
||||
GamesToFarm.Add(new Game(appID, name, hours, cardsRemaining, badgeLevel));
|
||||
} else {
|
||||
Task task = CheckGame(appID, name, hours);
|
||||
Task task = CheckGame(appID, name, hours, badgeLevel);
|
||||
switch (Program.GlobalConfig.OptimizationMode) {
|
||||
case GlobalConfig.EOptimizationMode.MinMemoryUsage:
|
||||
await task.ConfigureAwait(false);
|
||||
@@ -523,42 +588,77 @@ namespace ArchiSteamFarm {
|
||||
// If we have restricted card drops, we use complex algorithm
|
||||
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.ChosenFarmingAlgorithm, "Complex"));
|
||||
while (GamesToFarm.Count > 0) {
|
||||
HashSet<Game> gamesToFarmSolo = GamesToFarm.Count > 1 ? new HashSet<Game>(GamesToFarm.Where(game => game.HoursPlayed >= HoursToBump)) : new HashSet<Game>(GamesToFarm);
|
||||
if (gamesToFarmSolo.Count > 0) {
|
||||
while (gamesToFarmSolo.Count > 0) {
|
||||
Game game = gamesToFarmSolo.First();
|
||||
if (await FarmSolo(game).ConfigureAwait(false)) {
|
||||
gamesToFarmSolo.Remove(game);
|
||||
} else {
|
||||
NowFarming = false;
|
||||
return;
|
||||
HashSet<Game> gamesToCheck = new HashSet<Game>(GamesToFarm.Count > 1 ? GamesToFarm.Where(game => game.HoursPlayed >= HoursToBump) : GamesToFarm);
|
||||
|
||||
if (gamesToCheck.Count > 0) {
|
||||
foreach (Game game in gamesToCheck) {
|
||||
if (!await IsPlayableGame(game).ConfigureAwait(false)) {
|
||||
GamesToFarm.Remove(game);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (FarmMultiple(GamesToFarm.OrderByDescending(game => game.HoursPlayed).Take(ArchiHandler.MaxGamesPlayedConcurrently))) {
|
||||
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.IdlingFinishedForGames, string.Join(", ", GamesToFarm.Select(game => game.AppID))));
|
||||
} else {
|
||||
|
||||
if (await FarmSolo(game).ConfigureAwait(false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NowFarming = false;
|
||||
return;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
gamesToCheck = new HashSet<Game>(GamesToFarm.OrderByDescending(game => game.HoursPlayed));
|
||||
HashSet<Game> playableGamesToFarmMultiple = new HashSet<Game>();
|
||||
|
||||
foreach (Game game in gamesToCheck) {
|
||||
if (!await IsPlayableGame(game).ConfigureAwait(false)) {
|
||||
GamesToFarm.Remove(game);
|
||||
continue;
|
||||
}
|
||||
|
||||
playableGamesToFarmMultiple.Add(game);
|
||||
if (playableGamesToFarmMultiple.Count >= ArchiHandler.MaxGamesPlayedConcurrently) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (playableGamesToFarmMultiple.Count == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (await FarmMultiple(playableGamesToFarmMultiple).ConfigureAwait(false)) {
|
||||
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.IdlingFinishedForGames, string.Join(", ", playableGamesToFarmMultiple.Select(game => game.AppID))));
|
||||
} else {
|
||||
NowFarming = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If we have unrestricted card drops, we use simple algorithm
|
||||
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.ChosenFarmingAlgorithm, "Simple"));
|
||||
while (GamesToFarm.Count > 0) {
|
||||
Game game = GamesToFarm.First();
|
||||
if (await FarmSolo(game).ConfigureAwait(false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NowFarming = false;
|
||||
return;
|
||||
while (GamesToFarm.Count > 0) {
|
||||
HashSet<Game> gamesToCheck = new HashSet<Game>(GamesToFarm);
|
||||
|
||||
foreach (Game game in gamesToCheck) {
|
||||
if (!await IsPlayableGame(game).ConfigureAwait(false)) {
|
||||
GamesToFarm.Remove(game);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (await FarmSolo(game).ConfigureAwait(false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NowFarming = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while ((await IsAnythingToFarm().ConfigureAwait(false)).GetValueOrDefault());
|
||||
|
||||
CurrentGamesFarming.ClearAndTrim();
|
||||
CurrentGamesFarming.Clear();
|
||||
NowFarming = false;
|
||||
|
||||
Bot.ArchiLogger.LogGenericInfo(Strings.IdlingFinished);
|
||||
@@ -573,44 +673,37 @@ namespace ArchiSteamFarm {
|
||||
|
||||
bool success = true;
|
||||
|
||||
uint appID = await Bot.GetAppIDForIdling(game.AppID).ConfigureAwait(false);
|
||||
if (appID != 0) {
|
||||
if (appID != game.AppID) {
|
||||
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningIdlingGameMismatch, game.AppID, game.GameName, appID));
|
||||
if (game.AppID != game.PlayableAppID) {
|
||||
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningIdlingGameMismatch, game.AppID, game.GameName, game.PlayableAppID));
|
||||
}
|
||||
|
||||
await Bot.IdleGames(game.PlayableAppID.ToEnumerable()).ConfigureAwait(false);
|
||||
DateTime endFarmingDate = DateTime.UtcNow.AddHours(Program.GlobalConfig.MaxFarmingTime);
|
||||
|
||||
bool? keepFarming = await ShouldFarm(game).ConfigureAwait(false);
|
||||
while (keepFarming.GetValueOrDefault(true) && (DateTime.UtcNow < endFarmingDate)) {
|
||||
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.StillIdling, game.AppID, game.GameName));
|
||||
|
||||
DateTime startFarmingPeriod = DateTime.UtcNow;
|
||||
if (await FarmingResetSemaphore.WaitAsync(60 * 1000 * Program.GlobalConfig.FarmingDelay).ConfigureAwait(false)) {
|
||||
success = KeepFarming;
|
||||
}
|
||||
|
||||
Bot.PlayGame(appID, Bot.BotConfig.CustomGamePlayedWhileFarming);
|
||||
DateTime endFarmingDate = DateTime.UtcNow.AddHours(Program.GlobalConfig.MaxFarmingTime);
|
||||
// Don't forget to update our GamesToFarm hours
|
||||
game.HoursPlayed += (float) DateTime.UtcNow.Subtract(startFarmingPeriod).TotalHours;
|
||||
|
||||
bool? keepFarming = await ShouldFarm(game).ConfigureAwait(false);
|
||||
while (keepFarming.GetValueOrDefault(true) && (DateTime.UtcNow < endFarmingDate)) {
|
||||
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.StillIdling, game.AppID, game.GameName));
|
||||
|
||||
DateTime startFarmingPeriod = DateTime.UtcNow;
|
||||
if (FarmResetEvent.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
|
||||
FarmResetEvent.Reset();
|
||||
success = KeepFarming;
|
||||
}
|
||||
|
||||
// Don't forget to update our GamesToFarm hours
|
||||
game.HoursPlayed += (float) DateTime.UtcNow.Subtract(startFarmingPeriod).TotalHours;
|
||||
|
||||
if (!success) {
|
||||
break;
|
||||
}
|
||||
|
||||
keepFarming = await ShouldFarm(game).ConfigureAwait(false);
|
||||
if (!success) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
IgnoredAppIDs[game.AppID] = DateTime.UtcNow;
|
||||
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.IdlingGameNotPossible, game.AppID, game.GameName));
|
||||
|
||||
keepFarming = await ShouldFarm(game).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.StoppedIdling, game.AppID, game.GameName));
|
||||
return success;
|
||||
}
|
||||
|
||||
private bool FarmHours(ConcurrentHashSet<Game> games) {
|
||||
private async Task<bool> FarmHours(ConcurrentHashSet<Game> games) {
|
||||
if ((games == null) || (games.Count == 0)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(games));
|
||||
return false;
|
||||
@@ -627,15 +720,14 @@ namespace ArchiSteamFarm {
|
||||
return true;
|
||||
}
|
||||
|
||||
Bot.PlayGames(games.Select(game => game.AppID), Bot.BotConfig.CustomGamePlayedWhileFarming);
|
||||
await Bot.IdleGames(games.Select(game => game.PlayableAppID)).ConfigureAwait(false);
|
||||
|
||||
bool success = true;
|
||||
while (maxHour < 2) {
|
||||
while (maxHour < HoursToBump) {
|
||||
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.StillIdlingList, string.Join(", ", games.Select(game => game.AppID))));
|
||||
|
||||
DateTime startFarmingPeriod = DateTime.UtcNow;
|
||||
if (FarmResetEvent.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
|
||||
FarmResetEvent.Reset();
|
||||
if (await FarmingResetSemaphore.WaitAsync(60 * 1000 * Program.GlobalConfig.FarmingDelay).ConfigureAwait(false)) {
|
||||
success = KeepFarming;
|
||||
}
|
||||
|
||||
@@ -656,7 +748,7 @@ namespace ArchiSteamFarm {
|
||||
return success;
|
||||
}
|
||||
|
||||
private bool FarmMultiple(IEnumerable<Game> games) {
|
||||
private async Task<bool> FarmMultiple(IEnumerable<Game> games) {
|
||||
if (games == null) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(games));
|
||||
return false;
|
||||
@@ -666,8 +758,8 @@ namespace ArchiSteamFarm {
|
||||
|
||||
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.NowIdlingList, string.Join(", ", CurrentGamesFarming.Select(game => game.AppID))));
|
||||
|
||||
bool result = FarmHours(CurrentGamesFarming);
|
||||
CurrentGamesFarming.ClearAndTrim();
|
||||
bool result = await FarmHours(CurrentGamesFarming).ConfigureAwait(false);
|
||||
CurrentGamesFarming.Clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -682,7 +774,7 @@ namespace ArchiSteamFarm {
|
||||
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.NowIdling, game.AppID, game.GameName));
|
||||
|
||||
bool result = await Farm(game).ConfigureAwait(false);
|
||||
CurrentGamesFarming.ClearAndTrim();
|
||||
CurrentGamesFarming.Clear();
|
||||
|
||||
if (!result) {
|
||||
return false;
|
||||
@@ -751,7 +843,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
GamesToFarm.ClearAndTrim();
|
||||
GamesToFarm.Clear();
|
||||
|
||||
List<Task> tasks = new List<Task>();
|
||||
Task mainTask = CheckPage(htmlDocument);
|
||||
@@ -791,13 +883,27 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
if (GamesToFarm.Count == 0) {
|
||||
ShouldResumeFarming = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
ShouldResumeFarming = true;
|
||||
SortGamesToFarm();
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<bool> IsPlayableGame(Game game) {
|
||||
(uint PlayableAppID, DateTime IgnoredUntil) appData = await Bot.GetAppDataForIdling(game.AppID, game.HoursPlayed).ConfigureAwait(false);
|
||||
if (appData.PlayableAppID != 0) {
|
||||
game.PlayableAppID = appData.PlayableAppID;
|
||||
return true;
|
||||
}
|
||||
|
||||
IgnoredAppIDs[game.AppID] = appData.IgnoredUntil != DateTime.MaxValue ? appData.IgnoredUntil : DateTime.UtcNow.AddHours(HoursToIgnore);
|
||||
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.IdlingGameNotPossible, game.AppID, game.GameName));
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<bool?> ShouldFarm(Game game) {
|
||||
if (game == null) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(game));
|
||||
@@ -817,50 +923,94 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
private void SortGamesToFarm() {
|
||||
IOrderedEnumerable<Game> gamesToFarm;
|
||||
IOrderedEnumerable<Game> gamesToFarm = GamesToFarm.OrderBy(game => Bot.IsPriorityIdling(game.AppID) ? 1 : 0);
|
||||
|
||||
switch (Bot.BotConfig.FarmingOrder) {
|
||||
case BotConfig.EFarmingOrder.Unordered:
|
||||
return;
|
||||
break;
|
||||
case BotConfig.EFarmingOrder.AppIDsAscending:
|
||||
gamesToFarm = GamesToFarm.OrderBy(game => game.AppID);
|
||||
gamesToFarm = gamesToFarm.ThenBy(game => game.AppID);
|
||||
break;
|
||||
case BotConfig.EFarmingOrder.AppIDsDescending:
|
||||
gamesToFarm = GamesToFarm.OrderByDescending(game => game.AppID);
|
||||
gamesToFarm = gamesToFarm.ThenByDescending(game => game.AppID);
|
||||
break;
|
||||
case BotConfig.EFarmingOrder.BadgeLevelsAscending:
|
||||
gamesToFarm = gamesToFarm.ThenBy(game => game.BadgeLevel);
|
||||
break;
|
||||
case BotConfig.EFarmingOrder.BadgeLevelsDescending:
|
||||
gamesToFarm = gamesToFarm.ThenByDescending(game => game.BadgeLevel);
|
||||
break;
|
||||
case BotConfig.EFarmingOrder.CardDropsAscending:
|
||||
gamesToFarm = GamesToFarm.OrderBy(game => game.CardsRemaining);
|
||||
gamesToFarm = gamesToFarm.ThenBy(game => game.CardsRemaining);
|
||||
break;
|
||||
case BotConfig.EFarmingOrder.CardDropsDescending:
|
||||
gamesToFarm = GamesToFarm.OrderByDescending(game => game.CardsRemaining);
|
||||
gamesToFarm = gamesToFarm.ThenByDescending(game => game.CardsRemaining);
|
||||
break;
|
||||
case BotConfig.EFarmingOrder.HoursAscending:
|
||||
gamesToFarm = GamesToFarm.OrderBy(game => game.HoursPlayed);
|
||||
gamesToFarm = gamesToFarm.ThenBy(game => game.HoursPlayed);
|
||||
break;
|
||||
case BotConfig.EFarmingOrder.HoursDescending:
|
||||
gamesToFarm = GamesToFarm.OrderByDescending(game => game.HoursPlayed);
|
||||
gamesToFarm = gamesToFarm.ThenByDescending(game => game.HoursPlayed);
|
||||
break;
|
||||
case BotConfig.EFarmingOrder.NamesAscending:
|
||||
gamesToFarm = GamesToFarm.OrderBy(game => game.GameName);
|
||||
gamesToFarm = gamesToFarm.ThenBy(game => game.GameName);
|
||||
break;
|
||||
case BotConfig.EFarmingOrder.NamesDescending:
|
||||
gamesToFarm = GamesToFarm.OrderByDescending(game => game.GameName);
|
||||
gamesToFarm = gamesToFarm.ThenByDescending(game => game.GameName);
|
||||
break;
|
||||
case BotConfig.EFarmingOrder.Random:
|
||||
gamesToFarm = GamesToFarm.OrderBy(game => Utilities.RandomNext());
|
||||
gamesToFarm = gamesToFarm.ThenBy(game => Utilities.RandomNext());
|
||||
break;
|
||||
case BotConfig.EFarmingOrder.RedeemDateTimesAscending:
|
||||
case BotConfig.EFarmingOrder.RedeemDateTimesDescending:
|
||||
Dictionary<uint, DateTime> redeemDates = new Dictionary<uint, DateTime>(GamesToFarm.Count);
|
||||
|
||||
foreach (Game game in GamesToFarm) {
|
||||
DateTime redeemDate = DateTime.MinValue;
|
||||
if (Program.GlobalDatabase.AppIDsToPackageIDs.TryGetValue(game.AppID, out ConcurrentHashSet<uint> packageIDs)) {
|
||||
// ReSharper disable once LoopCanBePartlyConvertedToQuery - C# 7.0 out can't be used within LINQ query yet | https://github.com/dotnet/roslyn/issues/15619
|
||||
foreach (uint packageID in packageIDs) {
|
||||
if (!Bot.OwnedPackageIDs.TryGetValue(packageID, out (EPaymentMethod PaymentMethod, DateTime TimeCreated) packageData)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (packageData.TimeCreated > redeemDate) {
|
||||
redeemDate = packageData.TimeCreated;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
redeemDates[game.AppID] = redeemDate;
|
||||
}
|
||||
|
||||
switch (Bot.BotConfig.FarmingOrder) {
|
||||
case BotConfig.EFarmingOrder.RedeemDateTimesAscending:
|
||||
gamesToFarm = gamesToFarm.ThenBy(game => redeemDates[game.AppID]);
|
||||
break;
|
||||
case BotConfig.EFarmingOrder.RedeemDateTimesDescending:
|
||||
gamesToFarm = gamesToFarm.ThenByDescending(game => redeemDates[game.AppID]);
|
||||
break;
|
||||
default:
|
||||
Bot.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(Bot.BotConfig.FarmingOrder)));
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
Bot.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(Bot.BotConfig.FarmingOrder)));
|
||||
return;
|
||||
}
|
||||
|
||||
GamesToFarm.ReplaceWith(gamesToFarm.ToList()); // We must call ToList() here as we can't enumerate during replacing
|
||||
// We must call ToList() here as we can't enumerate during replacing
|
||||
GamesToFarm.ReplaceWith(gamesToFarm.ToList());
|
||||
}
|
||||
|
||||
internal sealed class Game {
|
||||
[JsonProperty]
|
||||
internal readonly uint AppID;
|
||||
|
||||
internal readonly byte BadgeLevel;
|
||||
|
||||
[JsonProperty]
|
||||
internal readonly string GameName;
|
||||
|
||||
@@ -870,9 +1020,9 @@ namespace ArchiSteamFarm {
|
||||
[JsonProperty]
|
||||
internal float HoursPlayed { get; set; }
|
||||
|
||||
//internal string HeaderURL => "https://steamcdn-a.akamaihd.net/steam/apps/" + AppID + "/header.jpg";
|
||||
internal uint PlayableAppID { get; set; }
|
||||
|
||||
internal Game(uint appID, string gameName, float hoursPlayed, ushort cardsRemaining) {
|
||||
internal Game(uint appID, string gameName, float hoursPlayed, ushort cardsRemaining, byte badgeLevel) {
|
||||
if ((appID == 0) || string.IsNullOrEmpty(gameName) || (hoursPlayed < 0) || (cardsRemaining == 0)) {
|
||||
throw new ArgumentOutOfRangeException(nameof(appID) + " || " + nameof(gameName) + " || " + nameof(hoursPlayed) + " || " + nameof(cardsRemaining));
|
||||
}
|
||||
@@ -881,19 +1031,21 @@ namespace ArchiSteamFarm {
|
||||
GameName = gameName;
|
||||
HoursPlayed = hoursPlayed;
|
||||
CardsRemaining = cardsRemaining;
|
||||
BadgeLevel = badgeLevel;
|
||||
|
||||
PlayableAppID = appID;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
if (obj == null) {
|
||||
if (ReferenceEquals(null, obj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj == this) {
|
||||
if (ReferenceEquals(this, obj)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Game game = obj as Game;
|
||||
return (game != null) && Equals(game);
|
||||
return obj is Game game && Equals(game);
|
||||
}
|
||||
|
||||
public override int GetHashCode() => (int) AppID;
|
||||
|
||||
@@ -25,27 +25,29 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Nito.AsyncEx;
|
||||
using System.Threading;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class ConcurrentEnumerator<T> : IEnumerator<T> {
|
||||
public T Current => Enumerator.Current;
|
||||
|
||||
private readonly IEnumerator<T> Enumerator;
|
||||
private readonly IDisposable Lock;
|
||||
private readonly SemaphoreSlim SemaphoreSlim;
|
||||
|
||||
object IEnumerator.Current => Current;
|
||||
|
||||
internal ConcurrentEnumerator(ICollection<T> collection, AsyncReaderWriterLock rwLock) {
|
||||
if ((collection == null) || (rwLock == null)) {
|
||||
throw new ArgumentNullException(nameof(collection) + " || " + nameof(rwLock));
|
||||
internal ConcurrentEnumerator(ICollection<T> collection, SemaphoreSlim semaphoreSlim) {
|
||||
if ((collection == null) || (semaphoreSlim == null)) {
|
||||
throw new ArgumentNullException(nameof(collection) + " || " + nameof(semaphoreSlim));
|
||||
}
|
||||
|
||||
Lock = rwLock.ReaderLock();
|
||||
SemaphoreSlim = semaphoreSlim;
|
||||
SemaphoreSlim.Wait();
|
||||
|
||||
Enumerator = collection.GetEnumerator();
|
||||
}
|
||||
|
||||
public void Dispose() => Lock.Dispose();
|
||||
public void Dispose() => SemaphoreSlim.Release();
|
||||
public bool MoveNext() => Enumerator.MoveNext();
|
||||
public void Reset() => Enumerator.Reset();
|
||||
}
|
||||
|
||||
@@ -23,168 +23,103 @@
|
||||
*/
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Nito.AsyncEx;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ISet<T> {
|
||||
public int Count {
|
||||
get {
|
||||
using (Lock.ReaderLock()) {
|
||||
return HashSet.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Count => BackingCollection.Count;
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
private readonly HashSet<T> HashSet = new HashSet<T>();
|
||||
private readonly AsyncReaderWriterLock Lock = new AsyncReaderWriterLock();
|
||||
private readonly ConcurrentDictionary<T, bool> BackingCollection = new ConcurrentDictionary<T, bool>();
|
||||
|
||||
public bool Add(T item) {
|
||||
using (Lock.WriterLock()) {
|
||||
return HashSet.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear() {
|
||||
using (Lock.WriterLock()) {
|
||||
HashSet.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(T item) {
|
||||
using (Lock.ReaderLock()) {
|
||||
return HashSet.Contains(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex) {
|
||||
using (Lock.ReaderLock()) {
|
||||
HashSet.CopyTo(array, arrayIndex);
|
||||
}
|
||||
}
|
||||
public bool Add(T item) => BackingCollection.TryAdd(item, true);
|
||||
public void Clear() => BackingCollection.Clear();
|
||||
public bool Contains(T item) => BackingCollection.ContainsKey(item);
|
||||
public void CopyTo(T[] array, int arrayIndex) => BackingCollection.Keys.CopyTo(array, arrayIndex);
|
||||
|
||||
public void ExceptWith(IEnumerable<T> other) {
|
||||
using (Lock.WriterLock()) {
|
||||
HashSet.ExceptWith(other);
|
||||
foreach (T item in other) {
|
||||
Remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator() => new ConcurrentEnumerator<T>(HashSet, Lock);
|
||||
public IEnumerator<T> GetEnumerator() => BackingCollection.Keys.GetEnumerator();
|
||||
|
||||
public void IntersectWith(IEnumerable<T> other) {
|
||||
using (Lock.WriterLock()) {
|
||||
HashSet.IntersectWith(other);
|
||||
ICollection<T> collection = other as ICollection<T> ?? other.ToList();
|
||||
foreach (T item in this.Where(item => !collection.Contains(item))) {
|
||||
Remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsProperSubsetOf(IEnumerable<T> other) {
|
||||
using (Lock.ReaderLock()) {
|
||||
return HashSet.IsProperSubsetOf(other);
|
||||
}
|
||||
ICollection<T> collection = other as ICollection<T> ?? other.ToList();
|
||||
return (collection.Count != Count) && IsSubsetOf(collection);
|
||||
}
|
||||
|
||||
public bool IsProperSupersetOf(IEnumerable<T> other) {
|
||||
using (Lock.ReaderLock()) {
|
||||
return HashSet.IsProperSupersetOf(other);
|
||||
}
|
||||
ICollection<T> collection = other as ICollection<T> ?? other.ToList();
|
||||
return (collection.Count != Count) && IsSupersetOf(collection);
|
||||
}
|
||||
|
||||
public bool IsSubsetOf(IEnumerable<T> other) {
|
||||
using (Lock.ReaderLock()) {
|
||||
return HashSet.IsSubsetOf(other);
|
||||
}
|
||||
ICollection<T> collection = other as ICollection<T> ?? other.ToList();
|
||||
return this.AsParallel().All(collection.Contains);
|
||||
}
|
||||
|
||||
public bool IsSupersetOf(IEnumerable<T> other) {
|
||||
using (Lock.ReaderLock()) {
|
||||
return HashSet.IsSupersetOf(other);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Overlaps(IEnumerable<T> other) {
|
||||
using (Lock.ReaderLock()) {
|
||||
return HashSet.Overlaps(other);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(T item) {
|
||||
using (Lock.WriterLock()) {
|
||||
return HashSet.Remove(item);
|
||||
}
|
||||
}
|
||||
public bool IsSupersetOf(IEnumerable<T> other) => other.AsParallel().All(Contains);
|
||||
public bool Overlaps(IEnumerable<T> other) => other.AsParallel().Any(Contains);
|
||||
public bool Remove(T item) => BackingCollection.TryRemove(item, out _);
|
||||
|
||||
public bool SetEquals(IEnumerable<T> other) {
|
||||
using (Lock.ReaderLock()) {
|
||||
return HashSet.SetEquals(other);
|
||||
}
|
||||
ICollection<T> collection = other as ICollection<T> ?? other.ToList();
|
||||
return (collection.Count == Count) && collection.AsParallel().All(Contains);
|
||||
}
|
||||
|
||||
public void SymmetricExceptWith(IEnumerable<T> other) {
|
||||
using (Lock.WriterLock()) {
|
||||
HashSet.SymmetricExceptWith(other);
|
||||
ICollection<T> collection = other as ICollection<T> ?? other.ToList();
|
||||
|
||||
HashSet<T> removed = new HashSet<T>();
|
||||
foreach (T item in collection.Where(Contains)) {
|
||||
removed.Add(item);
|
||||
Remove(item);
|
||||
}
|
||||
|
||||
foreach (T item in collection.Where(item => !removed.Contains(item))) {
|
||||
Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnionWith(IEnumerable<T> other) {
|
||||
using (Lock.WriterLock()) {
|
||||
HashSet.UnionWith(other);
|
||||
foreach (T otherElement in other) {
|
||||
Add(otherElement);
|
||||
}
|
||||
}
|
||||
|
||||
void ICollection<T>.Add(T item) => Add(item);
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
internal bool AddRange(IEnumerable<T> items) {
|
||||
using (Lock.WriterLock()) {
|
||||
// We use Count() and not Any() because we must ensure full loop pass
|
||||
return items.Count(item => HashSet.Add(item)) > 0;
|
||||
// We use Count() and not Any() because we must ensure full loop pass
|
||||
internal bool AddRange(IEnumerable<T> items) => items.Count(Add) > 0;
|
||||
|
||||
// We use Count() and not Any() because we must ensure full loop pass
|
||||
internal bool RemoveRange(IEnumerable<T> items) => items.Count(Remove) > 0;
|
||||
|
||||
internal bool ReplaceIfNeededWith(ICollection<T> other) {
|
||||
if (SetEquals(other)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ReplaceWith(other);
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void ClearAndTrim() {
|
||||
using (Lock.WriterLock()) {
|
||||
HashSet.Clear();
|
||||
HashSet.TrimExcess();
|
||||
}
|
||||
}
|
||||
|
||||
internal bool RemoveRange(IEnumerable<T> items) {
|
||||
using (Lock.WriterLock()) {
|
||||
// We use Count() and not Any() because we must ensure full loop pass
|
||||
return items.Count(item => HashSet.Remove(item)) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool ReplaceIfNeededWith(ICollection<T> items) {
|
||||
using (Lock.WriterLock()) {
|
||||
if (HashSet.SetEquals(items)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HashSet.Clear();
|
||||
|
||||
foreach (T item in items) {
|
||||
HashSet.Add(item);
|
||||
}
|
||||
|
||||
HashSet.TrimExcess();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal void ReplaceWith(IEnumerable<T> items) {
|
||||
using (Lock.WriterLock()) {
|
||||
HashSet.Clear();
|
||||
|
||||
foreach (T item in items) {
|
||||
HashSet.Add(item);
|
||||
}
|
||||
|
||||
HashSet.TrimExcess();
|
||||
internal void ReplaceWith(IEnumerable<T> other) {
|
||||
BackingCollection.Clear();
|
||||
foreach (T item in other) {
|
||||
BackingCollection[item] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
220
ArchiSteamFarm/ConcurrentSortedHashSet.cs
Normal file
220
ArchiSteamFarm/ConcurrentSortedHashSet.cs
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2017 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class ConcurrentSortedHashSet<T> : IDisposable, IReadOnlyCollection<T>, ISet<T> {
|
||||
public int Count {
|
||||
get {
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
return BackingCollection.Count;
|
||||
} finally {
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
private readonly HashSet<T> BackingCollection = new HashSet<T>();
|
||||
private readonly SemaphoreSlim CollectionSemaphore = new SemaphoreSlim(1, 1);
|
||||
|
||||
public bool Add(T item) {
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
return BackingCollection.Add(item);
|
||||
} finally {
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear() {
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
BackingCollection.Clear();
|
||||
} finally {
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(T item) {
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
return BackingCollection.Contains(item);
|
||||
} finally {
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex) {
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
BackingCollection.CopyTo(array, arrayIndex);
|
||||
} finally {
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() => CollectionSemaphore.Dispose();
|
||||
|
||||
public void ExceptWith(IEnumerable<T> other) {
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
BackingCollection.ExceptWith(other);
|
||||
} finally {
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator() => new ConcurrentEnumerator<T>(BackingCollection, CollectionSemaphore);
|
||||
|
||||
public void IntersectWith(IEnumerable<T> other) {
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
BackingCollection.IntersectWith(other);
|
||||
} finally {
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsProperSubsetOf(IEnumerable<T> other) {
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
return BackingCollection.IsProperSubsetOf(other);
|
||||
} finally {
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsProperSupersetOf(IEnumerable<T> other) {
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
return BackingCollection.IsProperSupersetOf(other);
|
||||
} finally {
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSubsetOf(IEnumerable<T> other) {
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
return BackingCollection.IsSubsetOf(other);
|
||||
} finally {
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSupersetOf(IEnumerable<T> other) {
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
return BackingCollection.IsSupersetOf(other);
|
||||
} finally {
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Overlaps(IEnumerable<T> other) {
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
return BackingCollection.Overlaps(other);
|
||||
} finally {
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(T item) {
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
return BackingCollection.Remove(item);
|
||||
} finally {
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetEquals(IEnumerable<T> other) {
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
return BackingCollection.SetEquals(other);
|
||||
} finally {
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public void SymmetricExceptWith(IEnumerable<T> other) {
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
BackingCollection.SymmetricExceptWith(other);
|
||||
} finally {
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public void UnionWith(IEnumerable<T> other) {
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
BackingCollection.UnionWith(other);
|
||||
} finally {
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
void ICollection<T>.Add(T item) => Add(item);
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
internal void ReplaceWith(IEnumerable<T> other) {
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
BackingCollection.Clear();
|
||||
|
||||
foreach (T item in other) {
|
||||
BackingCollection.Add(item);
|
||||
}
|
||||
} finally {
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,9 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using ArchiSteamFarm.Localization;
|
||||
using Newtonsoft.Json;
|
||||
using SteamKit2;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
@@ -40,8 +40,8 @@ namespace ArchiSteamFarm {
|
||||
internal const byte DefaultLoginLimiterDelay = 10;
|
||||
internal const string UlongStringPrefix = "s_";
|
||||
|
||||
// This is hardcoded blacklist which should not be possible to change
|
||||
internal static readonly HashSet<uint> GlobalBlacklist = new HashSet<uint> { 267420, 303700, 335590, 368020, 425280, 480730, 566020, 639900 };
|
||||
internal static readonly HashSet<uint> GamesBlacklist = new HashSet<uint> { 402590 }; // Games with broken/unobtainable card drops
|
||||
internal static readonly HashSet<uint> SalesBlacklist = new HashSet<uint> { 267420, 303700, 335590, 368020, 425280, 480730, 566020, 639900 }; // Steam Summer/Winter sales
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly bool AutoRestart = true;
|
||||
@@ -49,6 +49,9 @@ namespace ArchiSteamFarm {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly bool AutoUpdates = true;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly byte BackgroundGCPeriod;
|
||||
|
||||
[SuppressMessage("ReSharper", "CollectionNeverUpdated.Global")]
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly HashSet<uint> Blacklist = new HashSet<uint>();
|
||||
@@ -78,7 +81,7 @@ namespace ArchiSteamFarm {
|
||||
#pragma warning restore 649
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly byte IdleFarmingPeriod = 3;
|
||||
internal readonly byte IdleFarmingPeriod = 8;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly byte InventoryLimiterDelay = 3;
|
||||
@@ -102,7 +105,7 @@ namespace ArchiSteamFarm {
|
||||
internal readonly bool Statistics = true;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly ProtocolType SteamProtocol = ProtocolType.Tcp;
|
||||
internal readonly ProtocolTypes SteamProtocols = ProtocolTypes.WebSocket;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly EUpdateChannel UpdateChannel = EUpdateChannel.Stable;
|
||||
@@ -152,17 +155,6 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
// SK2 supports only TCP and UDP steam protocols
|
||||
// Ensure that user can't screw this up
|
||||
switch (globalConfig.SteamProtocol) {
|
||||
case ProtocolType.Tcp:
|
||||
case ProtocolType.Udp:
|
||||
break;
|
||||
default:
|
||||
ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorConfigPropertyInvalid, nameof(globalConfig.SteamProtocol), globalConfig.SteamProtocol));
|
||||
return null;
|
||||
}
|
||||
|
||||
// User might not know what he's doing
|
||||
// Ensure that he can't screw core ASF variables
|
||||
if (globalConfig.MaxFarmingTime == 0) {
|
||||
|
||||
@@ -23,19 +23,18 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class GlobalDatabase : IDisposable {
|
||||
private static readonly JsonSerializerSettings CustomSerializerSettings = new JsonSerializerSettings {
|
||||
Converters = new List<JsonConverter>(2) {
|
||||
new IPAddressConverter(),
|
||||
new IPEndPointConverter()
|
||||
}
|
||||
};
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly ConcurrentDictionary<uint, ConcurrentHashSet<uint>> AppIDsToPackageIDs = new ConcurrentDictionary<uint, ConcurrentHashSet<uint>>();
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly Guid Guid = Guid.NewGuid();
|
||||
@@ -43,22 +42,11 @@ namespace ArchiSteamFarm {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly InMemoryServerListProvider ServerListProvider = new InMemoryServerListProvider();
|
||||
|
||||
private readonly object FileLock = new object();
|
||||
private readonly SemaphoreSlim FileSemaphore = new SemaphoreSlim(1, 1);
|
||||
private readonly SemaphoreSlim PackagesRefreshSemaphore = new SemaphoreSlim(1, 1);
|
||||
|
||||
internal uint CellID {
|
||||
get => _CellID;
|
||||
set {
|
||||
if ((value == 0) || (_CellID == value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_CellID = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
private uint _CellID;
|
||||
[JsonProperty(PropertyName = "_CellID", Required = Required.DisallowNull)]
|
||||
internal uint CellID { get; private set; }
|
||||
|
||||
private string FilePath;
|
||||
|
||||
@@ -69,13 +57,20 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
FilePath = filePath;
|
||||
Save();
|
||||
Save().Wait();
|
||||
}
|
||||
|
||||
// This constructor is used only by deserializer
|
||||
private GlobalDatabase() => ServerListProvider.ServerListUpdated += OnServerListUpdated;
|
||||
|
||||
public void Dispose() => ServerListProvider.ServerListUpdated -= OnServerListUpdated;
|
||||
public void Dispose() {
|
||||
// Events we registered
|
||||
ServerListProvider.ServerListUpdated -= OnServerListUpdated;
|
||||
|
||||
// Those are objects that are always being created if constructor doesn't throw exception
|
||||
FileSemaphore.Dispose();
|
||||
PackagesRefreshSemaphore.Dispose();
|
||||
}
|
||||
|
||||
internal static GlobalDatabase Load(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
@@ -90,7 +85,7 @@ namespace ArchiSteamFarm {
|
||||
GlobalDatabase globalDatabase;
|
||||
|
||||
try {
|
||||
globalDatabase = JsonConvert.DeserializeObject<GlobalDatabase>(File.ReadAllText(filePath), CustomSerializerSettings);
|
||||
globalDatabase = JsonConvert.DeserializeObject<GlobalDatabase>(File.ReadAllText(filePath));
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
return null;
|
||||
@@ -105,31 +100,76 @@ namespace ArchiSteamFarm {
|
||||
return globalDatabase;
|
||||
}
|
||||
|
||||
private void OnServerListUpdated(object sender, EventArgs e) => Save();
|
||||
internal async Task RefreshPackageIDs(Bot bot, ICollection<uint> packageIDs) {
|
||||
if ((bot == null) || (packageIDs == null) || (packageIDs.Count == 0)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(bot) + " || " + nameof(packageIDs));
|
||||
return;
|
||||
}
|
||||
|
||||
private void Save() {
|
||||
string json = JsonConvert.SerializeObject(this, CustomSerializerSettings);
|
||||
await PackagesRefreshSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
try {
|
||||
HashSet<uint> missingPackageIDs = new HashSet<uint>(packageIDs.AsParallel().Where(packageID => AppIDsToPackageIDs.Values.All(packages => !packages.Contains(packageID))));
|
||||
if (missingPackageIDs.Count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<uint, HashSet<uint>> appIDsToPackageIDs = await bot.GetAppIDsToPackageIDs(missingPackageIDs).ConfigureAwait(false);
|
||||
if ((appIDsToPackageIDs == null) || (appIDsToPackageIDs.Count == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<uint, HashSet<uint>> appIDtoPackageID in appIDsToPackageIDs) {
|
||||
if (!AppIDsToPackageIDs.TryGetValue(appIDtoPackageID.Key, out ConcurrentHashSet<uint> packages)) {
|
||||
packages = new ConcurrentHashSet<uint>();
|
||||
AppIDsToPackageIDs[appIDtoPackageID.Key] = packages;
|
||||
}
|
||||
|
||||
foreach (uint package in appIDtoPackageID.Value) {
|
||||
packages.Add(package);
|
||||
}
|
||||
}
|
||||
|
||||
await Save().ConfigureAwait(false);
|
||||
} finally {
|
||||
PackagesRefreshSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task SetCellID(uint value = 0) {
|
||||
if (value == CellID) {
|
||||
return;
|
||||
}
|
||||
|
||||
CellID = value;
|
||||
await Save().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async void OnServerListUpdated(object sender, EventArgs e) => await Save().ConfigureAwait(false);
|
||||
|
||||
private async Task Save() {
|
||||
string json = JsonConvert.SerializeObject(this);
|
||||
if (string.IsNullOrEmpty(json)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(json));
|
||||
return;
|
||||
}
|
||||
|
||||
// This call verifies if JSON is alright
|
||||
// We don't wrap it in try catch as it should always be the case
|
||||
// And if it's not, we want to know about it (in a crash) and correct it in future version
|
||||
JsonConvert.DeserializeObject<GlobalDatabase>(json, CustomSerializerSettings);
|
||||
string newFilePath = FilePath + ".new";
|
||||
|
||||
lock (FileLock) {
|
||||
for (byte i = 0; i < 5; i++) {
|
||||
try {
|
||||
File.WriteAllText(FilePath, json);
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
}
|
||||
await FileSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
Thread.Sleep(1000);
|
||||
try {
|
||||
await File.WriteAllTextAsync(newFilePath, json).ConfigureAwait(false);
|
||||
|
||||
if (File.Exists(FilePath)) {
|
||||
File.Replace(newFilePath, FilePath, null);
|
||||
} else {
|
||||
File.Move(newFilePath, FilePath);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
} finally {
|
||||
FileSemaphore.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,18 +28,21 @@ using System.Threading;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class Hacks {
|
||||
private const byte GarbageCollectorDelay = 1;
|
||||
|
||||
private static Timer GarbageCollectionTimer;
|
||||
private static Timer GarbageCompactionTimer;
|
||||
|
||||
internal static void Init() {
|
||||
internal static void EnableBackgroundGC(byte period) {
|
||||
if (period == 0) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(period));
|
||||
return;
|
||||
}
|
||||
|
||||
if (GarbageCollectionTimer == null) {
|
||||
GarbageCollectionTimer = new Timer(
|
||||
e => GC.Collect(),
|
||||
null,
|
||||
TimeSpan.FromSeconds(GarbageCollectorDelay), // Delay
|
||||
TimeSpan.FromSeconds(GarbageCollectorDelay) // Period
|
||||
TimeSpan.FromSeconds(period), // Delay
|
||||
TimeSpan.FromSeconds(period) // Period
|
||||
);
|
||||
}
|
||||
|
||||
@@ -47,8 +50,8 @@ namespace ArchiSteamFarm {
|
||||
GarbageCompactionTimer = new Timer(
|
||||
e => GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce,
|
||||
null,
|
||||
TimeSpan.FromMinutes(GarbageCollectorDelay), // Delay
|
||||
TimeSpan.FromMinutes(GarbageCollectorDelay) // Period
|
||||
TimeSpan.FromMinutes(period), // Delay
|
||||
TimeSpan.FromMinutes(period) // Period
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,16 +45,24 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (host) {
|
||||
case "0.0.0.0":
|
||||
case "::":
|
||||
// Silently map INADDR_ANY to match HttpListener expectations
|
||||
host = "*";
|
||||
break;
|
||||
}
|
||||
|
||||
string url = "http://" + host + ":" + port + "/" + nameof(IPC) + "/";
|
||||
HttpListener.Prefixes.Add(url);
|
||||
}
|
||||
|
||||
internal static void Start() {
|
||||
if (KeepRunning) {
|
||||
if (KeepRunning || (HttpListener.Prefixes.Count == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogGenericInfo(string.Format(Strings.IPCStarting, HttpListener.Prefixes.FirstOrDefault()));
|
||||
ASF.ArchiLogger.LogGenericInfo(string.Format(Strings.IPCStarting, HttpListener.Prefixes.First()));
|
||||
|
||||
try {
|
||||
HttpListener.Start();
|
||||
@@ -84,56 +92,60 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Program.GlobalConfig.SteamOwnerID == 0) {
|
||||
ASF.ArchiLogger.LogGenericInfo(Strings.ErrorIPCAccessDenied);
|
||||
await context.Response.WriteAsync(HttpStatusCode.Forbidden, Strings.ErrorIPCAccessDenied).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (Program.GlobalConfig.SteamOwnerID == 0) {
|
||||
ASF.ArchiLogger.LogGenericWarning(Strings.ErrorIPCAccessDenied);
|
||||
await context.Response.WriteAsync(HttpStatusCode.Forbidden, Strings.ErrorIPCAccessDenied).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!context.Request.RawUrl.StartsWith("/" + nameof(IPC), StringComparison.Ordinal)) {
|
||||
await context.Response.WriteAsync(HttpStatusCode.BadRequest, nameof(HttpStatusCode.BadRequest)).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (context.Request.HttpMethod) {
|
||||
case WebRequestMethods.Http.Get:
|
||||
for (int i = 0; i < context.Request.QueryString.Count; i++) {
|
||||
string key = context.Request.QueryString.GetKey(i);
|
||||
|
||||
switch (key) {
|
||||
case "command":
|
||||
string command = context.Request.QueryString.Get(i);
|
||||
if (string.IsNullOrEmpty(command)) {
|
||||
break;
|
||||
}
|
||||
|
||||
Bot bot = Bot.Bots.Values.FirstOrDefault();
|
||||
if (bot == null) {
|
||||
await context.Response.WriteAsync(HttpStatusCode.NotAcceptable, Strings.ErrorNoBotsDefined).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (command[0] != '!') {
|
||||
command = "!" + command;
|
||||
}
|
||||
|
||||
string response = await bot.Response(Program.GlobalConfig.SteamOwnerID, command).ConfigureAwait(false);
|
||||
|
||||
ASF.ArchiLogger.LogGenericInfo(string.Format(Strings.IPCAnswered, command, response));
|
||||
|
||||
await context.Response.WriteAsync(HttpStatusCode.OK, response).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
if (!context.Request.RawUrl.StartsWith("/" + nameof(IPC), StringComparison.Ordinal)) {
|
||||
await context.Response.WriteAsync(HttpStatusCode.BadRequest, nameof(HttpStatusCode.BadRequest)).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (context.Response.ContentLength64 == 0) {
|
||||
await context.Response.WriteAsync(HttpStatusCode.MethodNotAllowed, nameof(HttpStatusCode.MethodNotAllowed)).ConfigureAwait(false);
|
||||
switch (context.Request.HttpMethod) {
|
||||
case WebRequestMethods.Http.Get:
|
||||
for (int i = 0; i < context.Request.QueryString.Count; i++) {
|
||||
string key = context.Request.QueryString.GetKey(i);
|
||||
|
||||
switch (key) {
|
||||
case "command":
|
||||
string command = context.Request.QueryString.Get(i);
|
||||
if (string.IsNullOrWhiteSpace(command)) {
|
||||
break;
|
||||
}
|
||||
|
||||
Bot bot = Bot.Bots.Values.FirstOrDefault();
|
||||
if (bot == null) {
|
||||
await context.Response.WriteAsync(HttpStatusCode.NotAcceptable, Strings.ErrorNoBotsDefined).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (command[0] != '!') {
|
||||
command = "!" + command;
|
||||
}
|
||||
|
||||
string response = await bot.Response(Program.GlobalConfig.SteamOwnerID, command).ConfigureAwait(false);
|
||||
|
||||
ASF.ArchiLogger.LogGenericInfo(string.Format(Strings.IPCAnswered, command, response));
|
||||
|
||||
await context.Response.WriteAsync(HttpStatusCode.OK, response).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
await context.Response.WriteAsync(HttpStatusCode.BadRequest, nameof(HttpStatusCode.BadRequest)).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.Response.ContentLength64 == 0) {
|
||||
await context.Response.WriteAsync(HttpStatusCode.MethodNotAllowed, nameof(HttpStatusCode.MethodNotAllowed)).ConfigureAwait(false);
|
||||
}
|
||||
} finally {
|
||||
context.Response.Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,8 +160,7 @@ namespace ArchiSteamFarm {
|
||||
continue;
|
||||
}
|
||||
|
||||
await HandleRequest(context).ConfigureAwait(false);
|
||||
context.Response.Close();
|
||||
Utilities.StartBackgroundFunction(() => HandleRequest(context), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,8 +175,16 @@ namespace ArchiSteamFarm {
|
||||
response.StatusCode = (ushort) statusCode;
|
||||
}
|
||||
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(message);
|
||||
response.AppendHeader("Access-Control-Allow-Origin", "null");
|
||||
|
||||
Encoding encoding = Encoding.UTF8;
|
||||
|
||||
response.ContentEncoding = encoding;
|
||||
response.ContentType = "text/plain; charset=" + encoding.WebName;
|
||||
|
||||
byte[] buffer = encoding.GetBytes(message + Environment.NewLine);
|
||||
response.ContentLength64 = buffer.Length;
|
||||
|
||||
await response.OutputStream.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
|
||||
} catch (Exception e) {
|
||||
response.StatusCode = (ushort) HttpStatusCode.InternalServerError;
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2017 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class IPEndPointConverter : JsonConverter {
|
||||
public override bool CanConvert(Type objectType) => objectType == typeof(IPEndPoint);
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
|
||||
JObject jo = JObject.Load(reader);
|
||||
IPAddress address = jo["Address"].ToObject<IPAddress>(serializer);
|
||||
ushort port = jo["Port"].Value<ushort>();
|
||||
return new IPEndPoint(address, port);
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
|
||||
IPEndPoint ep = (IPEndPoint) value;
|
||||
writer.WriteStartObject();
|
||||
writer.WritePropertyName("Address");
|
||||
serializer.Serialize(writer, ep.Address);
|
||||
writer.WritePropertyName("Port");
|
||||
writer.WriteValue(ep.Port);
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using SteamKit2.Discovery;
|
||||
@@ -32,19 +32,19 @@ using SteamKit2.Discovery;
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class InMemoryServerListProvider : IServerListProvider {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
private readonly ConcurrentHashSet<IPEndPoint> Servers = new ConcurrentHashSet<IPEndPoint>();
|
||||
private readonly ConcurrentHashSet<ServerRecordEndPoint> ServerRecords = new ConcurrentHashSet<ServerRecordEndPoint>();
|
||||
|
||||
public Task<IEnumerable<IPEndPoint>> FetchServerListAsync() => Task.FromResult<IEnumerable<IPEndPoint>>(Servers);
|
||||
public Task<IEnumerable<ServerRecord>> FetchServerListAsync() => Task.FromResult(ServerRecords.Select(server => ServerRecord.CreateServer(server.Host, server.Port, server.ProtocolTypes)));
|
||||
|
||||
public Task UpdateServerListAsync(IEnumerable<IPEndPoint> endPoints) {
|
||||
if (endPoints == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(endPoints));
|
||||
public Task UpdateServerListAsync(IEnumerable<ServerRecord> endpoints) {
|
||||
if (endpoints == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(endpoints));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
HashSet<IPEndPoint> newServers = new HashSet<IPEndPoint>(endPoints);
|
||||
HashSet<ServerRecordEndPoint> newServerRecords = new HashSet<ServerRecordEndPoint>(endpoints.Select(ep => new ServerRecordEndPoint(ep.GetHost(), (ushort) ep.GetPort(), ep.ProtocolTypes)));
|
||||
|
||||
if (!Servers.ReplaceIfNeededWith(newServers)) {
|
||||
if (!ServerRecords.ReplaceIfNeededWith(newServerRecords)) {
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,11 @@ namespace ArchiSteamFarm.JSON {
|
||||
internal readonly string Name;
|
||||
#pragma warning restore 649
|
||||
|
||||
#pragma warning disable 649
|
||||
[JsonProperty(PropertyName = "size", Required = Required.Always)]
|
||||
internal readonly uint Size;
|
||||
#pragma warning restore 649
|
||||
|
||||
// Deserialized from JSON
|
||||
private Asset() { }
|
||||
}
|
||||
|
||||
@@ -220,6 +220,18 @@ namespace ArchiSteamFarm.JSON {
|
||||
private ConfirmationResponse() { }
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||
internal sealed class GenericResponse {
|
||||
#pragma warning disable 649
|
||||
[JsonProperty(PropertyName = "success", Required = Required.Always)]
|
||||
internal readonly EResult Result;
|
||||
#pragma warning restore 649
|
||||
|
||||
// Deserialized from JSON
|
||||
private GenericResponse() { }
|
||||
}
|
||||
|
||||
// REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_Asset
|
||||
internal sealed class Item {
|
||||
internal const ushort SteamAppID = 753;
|
||||
@@ -227,13 +239,12 @@ namespace ArchiSteamFarm.JSON {
|
||||
|
||||
internal uint Amount { get; private set; }
|
||||
internal uint AppID { get; set; }
|
||||
internal ulong AssetID { get; private set; }
|
||||
internal ulong ClassID { get; private set; }
|
||||
internal ulong ContextID { get; set; }
|
||||
internal uint RealAppID { get; set; }
|
||||
internal EType Type { get; set; }
|
||||
|
||||
private ulong AssetID;
|
||||
|
||||
[JsonProperty(PropertyName = "amount", Required = Required.Always)]
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||
private string AmountString {
|
||||
@@ -486,7 +497,15 @@ namespace ArchiSteamFarm.JSON {
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool IsSteamCardsRequest() => ItemsToGive.All(item => (item.AppID == Item.SteamAppID) && (item.ContextID == Item.SteamCommunityContextID) && (item.Type == Item.EType.TradingCard));
|
||||
internal bool IsValidSteamItemsRequest(HashSet<Item.EType> acceptedTypes) {
|
||||
if ((acceptedTypes == null) || (acceptedTypes.Count == 0)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(acceptedTypes));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = ItemsToGive.All(item => (item.AppID == Item.SteamAppID) && (item.ContextID == Item.SteamCommunityContextID) && acceptedTypes.Contains(item.Type));
|
||||
return result;
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
internal enum ETradeOfferState : byte {
|
||||
|
||||
38
ArchiSteamFarm/Localization/Strings.Designer.cs
generated
38
ArchiSteamFarm/Localization/Strings.Designer.cs
generated
@@ -492,6 +492,15 @@ namespace ArchiSteamFarm.Localization {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wyszukuje zlokalizowany ciąg podobny do ciągu There are {0}/{1} bots that already own all of the games being checked..
|
||||
/// </summary>
|
||||
internal static string BotOwnsOverview {
|
||||
get {
|
||||
return ResourceManager.GetString("BotOwnsOverview", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wyszukuje zlokalizowany ciąg podobny do ciągu Rate limit exceeded; we will retry after {0} of cooldown....
|
||||
/// </summary>
|
||||
@@ -654,15 +663,6 @@ namespace ArchiSteamFarm.Localization {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wyszukuje zlokalizowany ciąg podobny do ciągu Unable to connect to Steam: {0}.
|
||||
/// </summary>
|
||||
internal static string BotUnableToConnect {
|
||||
get {
|
||||
return ResourceManager.GetString("BotUnableToConnect", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wyszukuje zlokalizowany ciąg podobny do ciągu Unable to login to Steam: {0}/{1}.
|
||||
/// </summary>
|
||||
@@ -864,15 +864,6 @@ namespace ArchiSteamFarm.Localization {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wyszukuje zlokalizowany ciąg podobny do ciągu IPC service could not be started because of AddressAccessDeniedException! If you want to use IPC service provided by ASF, consider starting ASF as administrator, or giving proper permissions!.
|
||||
/// </summary>
|
||||
internal static string ErrorIPCAddressAccessDeniedException {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorIPCAddressAccessDeniedException", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wyszukuje zlokalizowany ciąg podobny do ciągu {0} is empty!.
|
||||
/// </summary>
|
||||
@@ -927,15 +918,6 @@ namespace ArchiSteamFarm.Localization {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wyszukuje zlokalizowany ciąg podobny do ciągu Could not remove old ASF binary. Please remove {0} manually in order for update function to work!.
|
||||
/// </summary>
|
||||
internal static string ErrorRemovingOldBinary {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorRemovingOldBinary", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wyszukuje zlokalizowany ciąg podobny do ciągu Request failed after {0} attempts!.
|
||||
/// </summary>
|
||||
@@ -1306,7 +1288,7 @@ namespace ArchiSteamFarm.Localization {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wyszukuje zlokalizowany ciąg podobny do ciągu Downloading new version... While waiting, consider donating if you appreciate the work being done! :).
|
||||
/// Wyszukuje zlokalizowany ciąg podobny do ciągu Downloading new version: {0} ({1} MB)... While waiting, consider donating if you appreciate the work being done! :).
|
||||
/// </summary>
|
||||
internal static string UpdateDownloadingNewVersion {
|
||||
get {
|
||||
|
||||
@@ -157,7 +157,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="WarningFailed" xml:space="preserve">
|
||||
<value>فشل!</value>
|
||||
</data>
|
||||
@@ -191,9 +190,7 @@
|
||||
<data name="UpdateCheckingNewVersion" xml:space="preserve">
|
||||
<value>جاري التحقيق من وجود إصدار جديد...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>جاري تحميل الإصدار الجديد... بينما تنتظر، تبرع إذا اعجبك عملنا! :)</value>
|
||||
</data>
|
||||
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>تم الانتهاء من عملية التحديث!</value>
|
||||
</data>
|
||||
@@ -218,7 +215,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="Done" xml:space="preserve">
|
||||
|
||||
@@ -121,7 +121,10 @@
|
||||
<value>Приемане на замяната: {0}</value>
|
||||
<comment>{0} will be replaced by trade number</comment>
|
||||
</data>
|
||||
|
||||
<data name="AutoUpdateCheckInfo" xml:space="preserve">
|
||||
<value>ASF автоматично ще проверява за нови версии на всеки {0} час/а/.</value>
|
||||
<comment>{0} will be replaced by translated TimeSpan string (such as "24 hours")</comment>
|
||||
</data>
|
||||
<data name="Content" xml:space="preserve">
|
||||
<value>Съдържание: {0}</value>
|
||||
<comment>{0} will be replaced by content string. Please note that this string should include newline for formatting.</comment>
|
||||
@@ -167,10 +170,6 @@
|
||||
<value>Разборът {0} се провали!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Не може да се премахне старият ASF файл. Моля премахнете {0} ръчно, за може обновлението да сработи!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>Заявката не е изпълнена след {0} опити!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -178,14 +177,19 @@
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>Не успя да се провери за последната версия!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>Не може да се продължи с обновяването, защото няма такава възможност за сегашната Ви версия! Автоматично обновяване на тази версия не е възможно.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>Не може да се премине към обновление, защото тази версия не включва никакви файлове!</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>Получено е желание за промяна от потребителя, но процеса продължава в режим без възможност за промяна!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>Отказва да обработи заявката, защото SteamOwnerID не е зададено!</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>Излизане…</value>
|
||||
</data>
|
||||
@@ -236,7 +240,8 @@
|
||||
<value>Проверяване за нова версия...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>В момента се сваля новата версия... Докато чакате, помислете за дарение, ако оценявате свършената от нас работа! :)</value>
|
||||
<value>В момента се сваля новата версия: {0} ({1} MB)... Докато чакате, помислете за дарение, ако оценявате свършената от нас работа! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Обновлението приключи!</value>
|
||||
@@ -276,7 +281,10 @@
|
||||
<value>Моля, въведете недокументирана стойност от {0}: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>Моля, въведете Вашият IPC host: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>Получена е непонятна стойност за {0}, моля съобщете за това: {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -285,10 +293,17 @@
|
||||
<value>Играенето на повече от {0} игри едновременно е невъзможно, само първите {0} от {1} ще бъдат ползвани!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>Отговоряне на IPC командата: {0} с {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>IPC сървърът е готов!</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>Стартиране IPC сървър на {0}...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Този бот вече е спрян!</value>
|
||||
</data>
|
||||
@@ -379,13 +394,20 @@
|
||||
<data name="UnknownCommand" xml:space="preserve">
|
||||
<value>Непозната команда!</value>
|
||||
</data>
|
||||
|
||||
|
||||
<data name="WarningCouldNotCheckBadges" xml:space="preserve">
|
||||
<value>Не е възможно да се набави информация за значките, ще опитаме отново по-късно!</value>
|
||||
</data>
|
||||
<data name="WarningCouldNotCheckCardsStatus" xml:space="preserve">
|
||||
<value>Не може да се провери статуса на картите за: {0} ({1}), ще опитаме отново по-късно!</value>
|
||||
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
|
||||
</data>
|
||||
<data name="BotAcceptingGift" xml:space="preserve">
|
||||
<value>Приемане на подарък: {0}...</value>
|
||||
<comment>{0} will be replaced by giftID (number)</comment>
|
||||
</data>
|
||||
|
||||
<data name="BotAccountLimited" xml:space="preserve">
|
||||
<value>Този акаунт е ограничен, процесът по вадене на карти е невъзможен докато ограничението не бъде премахнато!</value>
|
||||
</data>
|
||||
<data name="BotAddLicense" xml:space="preserve">
|
||||
<value>ID: {0} | Статус: {1}</value>
|
||||
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by status string</comment>
|
||||
@@ -400,17 +422,32 @@
|
||||
<data name="BotAuthenticatorConverting" xml:space="preserve">
|
||||
<value>Превръщането на .maFile в ASF формат...</value>
|
||||
</data>
|
||||
|
||||
|
||||
<data name="BotAuthenticatorImportFinished" xml:space="preserve">
|
||||
<value>Успешно приключи въвеждането на мобилното потвърждение!</value>
|
||||
</data>
|
||||
<data name="BotAuthenticatorInvalidDeviceID" xml:space="preserve">
|
||||
<value>Вашият DeviceID е неправилен или не съществува!</value>
|
||||
</data>
|
||||
<data name="BotAuthenticatorToken" xml:space="preserve">
|
||||
<value>2FA Token: {0}</value>
|
||||
<comment>{0} will be replaced by generated 2FA token (string)</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="BotAutomaticIdlingNowPaused" xml:space="preserve">
|
||||
<value>Автоматичното вадене на карти е временно прекратено!</value>
|
||||
</data>
|
||||
<data name="BotAutomaticIdlingNowResumed" xml:space="preserve">
|
||||
<value>Автоматичното вадене на карти е възобновено!</value>
|
||||
</data>
|
||||
<data name="BotAutomaticIdlingPausedAlready" xml:space="preserve">
|
||||
<value>Автоматичното вадене на карти е вече временно прекратено!</value>
|
||||
</data>
|
||||
<data name="BotAutomaticIdlingPausedWithCountdown" xml:space="preserve">
|
||||
<value>Автоматичното вадене на карти е вече прекратено! Имате {0} за да стартирате игра.</value>
|
||||
<comment>{0} will be replaced by translated TimeSpan string (such as "5 minutes")</comment>
|
||||
</data>
|
||||
<data name="BotAutomaticIdlingResumedAlready" xml:space="preserve">
|
||||
<value>Автоматичното вадене на карти е вече възобновено!</value>
|
||||
</data>
|
||||
<data name="BotConnected" xml:space="preserve">
|
||||
<value>Свързан към Steam!</value>
|
||||
</data>
|
||||
@@ -424,30 +461,56 @@
|
||||
<value>[{0}] парола: {1}</value>
|
||||
<comment>{0} will be replaced by password encryption method (string), {1} will be replaced by encrypted password using that method (string)</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
<data name="BotInstanceNotStartingBecauseDisabled" xml:space="preserve">
|
||||
<value>Не се стартира този бот, защото е изключен във файлът с настройки!</value>
|
||||
</data>
|
||||
<data name="BotInvalidAuthenticatorDuringLogin" xml:space="preserve">
|
||||
<value>Получен е TwoFactorCodeMismatch код за грешка {0} пъти поред, това почти винаги означава невалидни ASF 2FA стойности, прекратяване!</value>
|
||||
<comment>{0} will be replaced by maximum allowed number of failed 2FA attempts</comment>
|
||||
</data>
|
||||
<data name="BotLoggedOff" xml:space="preserve">
|
||||
<value>Излязъл от Steam: {0}</value>
|
||||
<comment>{0} will be replaced by logging off reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotLoggedOn" xml:space="preserve">
|
||||
<value>Влезли сте успешно!</value>
|
||||
</data>
|
||||
<data name="BotLoggingIn" xml:space="preserve">
|
||||
<value>Влизане…</value>
|
||||
</data>
|
||||
|
||||
<data name="BotLogonSessionReplaced" xml:space="preserve">
|
||||
<value>Този потребител изглежда се ползва в друг ASF работещ в момента, това е неправилно, отказване да продължи работата му!</value>
|
||||
</data>
|
||||
<data name="BotLootingFailed" xml:space="preserve">
|
||||
<value>Предложението за замяна е неуспешно!</value>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="BotLootingMasterNotDefined" xml:space="preserve">
|
||||
<value>Предложението за размяна не може да бъде избратено, защото няма потребител с master разрешително!</value>
|
||||
</data>
|
||||
<data name="BotLootingNoLootableTypes" xml:space="preserve">
|
||||
<value>Нямата никакви видове грабене зададени!</value>
|
||||
</data>
|
||||
<data name="BotLootingNowDisabled" xml:space="preserve">
|
||||
<value>Грабенето сега е изключено!</value>
|
||||
</data>
|
||||
<data name="BotLootingNowEnabled" xml:space="preserve">
|
||||
<value>Грабенето сега е включено!</value>
|
||||
</data>
|
||||
<data name="BotLootingSuccess" xml:space="preserve">
|
||||
<value>Предложението за замяна е изпратено успешно!</value>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="BotLootingTemporarilyDisabled" xml:space="preserve">
|
||||
<value>Грабенето е временно изключено!</value>
|
||||
</data>
|
||||
<data name="BotLootingYourself" xml:space="preserve">
|
||||
<value>Не можете да ограбите себе си!</value>
|
||||
</data>
|
||||
<data name="BotNoASFAuthenticator" xml:space="preserve">
|
||||
<value>Този бот няма ASF 2FA активиран! Да не би да сте забравили да въведете своят authenticator като ASF 2FA?</value>
|
||||
</data>
|
||||
<data name="BotNotConnected" xml:space="preserve">
|
||||
<value>Този бот не е свързан!</value>
|
||||
</data>
|
||||
<data name="BotNotOwnedYet" xml:space="preserve">
|
||||
<value>Все още не се притежава: {0}</value>
|
||||
<comment>{0} will be replaced by query (string)</comment>
|
||||
@@ -456,7 +519,10 @@
|
||||
<value>Вече се притежава: {0} | {1}</value>
|
||||
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
|
||||
</data>
|
||||
|
||||
<data name="BotRateLimitExceeded" xml:space="preserve">
|
||||
<value>Ограничението надвишено; Ще повторим след {0} на "отброяване"...</value>
|
||||
<comment>{0} will be replaced by translated TimeSpan string (such as "25 minutes")</comment>
|
||||
</data>
|
||||
<data name="BotReconnecting" xml:space="preserve">
|
||||
<value>Повторно свързване…</value>
|
||||
</data>
|
||||
@@ -483,14 +549,12 @@
|
||||
<data name="BotStatusNotRunning" xml:space="preserve">
|
||||
<value>Ботът не работи.</value>
|
||||
</data>
|
||||
|
||||
<data name="BotStatusPaused" xml:space="preserve">
|
||||
<value>Ботът е паузиран или работи в ръчен режим.</value>
|
||||
</data>
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>В момента се ползва бот.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Не може да се свърже със Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Не може да се впише в Steam: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -507,38 +571,73 @@
|
||||
<value>Неуспешно поради грешка: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="BotConnectionLost" xml:space="preserve">
|
||||
<value>Връзката с мрежата на Steam е загубена. Повторно свързване...</value>
|
||||
</data>
|
||||
<data name="BotAccountFree" xml:space="preserve">
|
||||
<value>Акаунтът не е вече зает: ваденето на карти е възобновено!</value>
|
||||
</data>
|
||||
<data name="BotAccountOccupied" xml:space="preserve">
|
||||
<value>Акаунтът в момента се ползва: ASF ще възобнови ваденето на карти, когато е свободен...</value>
|
||||
</data>
|
||||
<data name="BotAutomaticIdlingPauseTimeout" xml:space="preserve">
|
||||
<value>Споделената библиотека не е стартирана през даденият период на време. Ваденето на карти е продължено!</value>
|
||||
</data>
|
||||
<data name="BotConnecting" xml:space="preserve">
|
||||
<value>Свързване…</value>
|
||||
</data>
|
||||
|
||||
|
||||
<data name="BotHeartBeatFailed" xml:space="preserve">
|
||||
<value>Неуспешно прекъсване на връзката на клиента. Премахва се този бот!</value>
|
||||
</data>
|
||||
<data name="BotSteamDirectoryInitializationFailed" xml:space="preserve">
|
||||
<value>Неуспешно стартиране на SteamDirectory: свързването с мрежата на Steam може да отнеме много повече време от обичайното!</value>
|
||||
</data>
|
||||
<data name="BotStopping" xml:space="preserve">
|
||||
<value>Спиране…</value>
|
||||
</data>
|
||||
|
||||
|
||||
<data name="ErrorBotConfigInvalid" xml:space="preserve">
|
||||
<value>Настройката на вашият бот е невалидна. Моля проверете съдържанието на {0} и опитайте отново!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorDatabaseInvalid" xml:space="preserve">
|
||||
<value>Постоянната база данни не може да бъде заредена, ако проблемът продължава, моля премахнете {0} за да се пресъздаде базата данни!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="Initializing" xml:space="preserve">
|
||||
<value>Стартиране {0}…</value>
|
||||
<comment>{0} will be replaced by service name that is being initialized</comment>
|
||||
</data>
|
||||
|
||||
|
||||
<data name="WarningPrivacyPolicy" xml:space="preserve">
|
||||
<value>Моля, прегледайте нашата секция в wiki за "лична неприкосновеност", ако сте загрижени за това, което всъщност прави ASF!</value>
|
||||
</data>
|
||||
<data name="Welcome" xml:space="preserve">
|
||||
<value>Това изглежда е първото стартиране на програмата, добре дошли!</value>
|
||||
</data>
|
||||
<data name="ErrorInvalidCurrentCulture" xml:space="preserve">
|
||||
<value>Задали сте невалиден CurrentCulture. ASF ще продължи работа със зададения по подразбиране!</value>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
<data name="TranslationIncomplete" xml:space="preserve">
|
||||
<value>ASF ще се опита да използва вашият предпочитан {0} език, но преводът на този език е завършен само в {1}. Може би можете да помогнете да подобрим ASF с превод за вашия език?</value>
|
||||
<comment>{0} will be replaced by culture code, such as "en-US", {1} will be replaced by completeness percentage, such as "78.5%"</comment>
|
||||
</data>
|
||||
<data name="IdlingGameNotPossible" xml:space="preserve">
|
||||
<value>Вадене на карти {0} ({1}) е временно забранено, тъй като ASG не е в състояние да играе тази игра в момента.</value>
|
||||
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
|
||||
</data>
|
||||
<data name="WarningIdlingGameMismatch" xml:space="preserve">
|
||||
<value>ASF откри несъответствие на ID за {0} ({1}) и ще използва ID на {2} вместо това.</value>
|
||||
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name, {2} will be replaced by game's ID (number)</comment>
|
||||
</data>
|
||||
<data name="BotVersion" xml:space="preserve">
|
||||
<value>{0} V{1}</value>
|
||||
<comment>{0} will be replaced by program's name (e.g. "ASF"), {1} will be replaced by program's version (e.g. "1.0.0.0"). This string typically has nothing to translate and you should leave it as it is, unless you need to change the format, e.g. in RTL languages.</comment>
|
||||
</data>
|
||||
|
||||
|
||||
<data name="BotAccountLocked" xml:space="preserve">
|
||||
<value>Този акаунт е заключен, ваденето на карти е недостъпно завинаги!</value>
|
||||
</data>
|
||||
<data name="BotStatusLocked" xml:space="preserve">
|
||||
<value>Бота е заключен и не може да пусне никакви карти чрез "играене".</value>
|
||||
</data>
|
||||
<data name="ErrorFunctionOnlyInHeadlessMode" xml:space="preserve">
|
||||
<value>Тази функция е възможна само в headless режим!</value>
|
||||
</data>
|
||||
@@ -549,8 +648,23 @@
|
||||
<data name="ErrorAccessDenied" xml:space="preserve">
|
||||
<value>Достъпът отказан!</value>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="WarningPreReleaseVersion" xml:space="preserve">
|
||||
<value>Вие използвате версия, която е по-нова от последната версия за актуализация. Моля, имайте предвид, че предварителните версии са посветени на потребителите, които знаят как да докладват за бъгове, да се справят с проблеми и да дават обратна връзка - няма да ви бъде осигурена техническа подръжка.</value>
|
||||
</data>
|
||||
<data name="BotStats" xml:space="preserve">
|
||||
<value>В момента се ползва памет: {0} MB.</value>
|
||||
<comment>{0} will be replaced by number (in megabytes) of memory being used</comment>
|
||||
</data>
|
||||
<data name="ClearingDiscoveryQueue" xml:space="preserve">
|
||||
<value>Изчистване на предложенията на Steam #{0}...</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="DoneClearingDiscoveryQueue" xml:space="preserve">
|
||||
<value>Завърши изчистването на предложенията на Steam #{0}.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>Има {0}/{1} ботове, които вече притежават всички игри, които се проверяват.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -173,10 +173,6 @@ StackTrace:
|
||||
<value>Analýza {0} selhala.</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Nepodařilo se odstranit starý binární soubor aplikace ASF. Chcete-li zajistit správné fungování funkce aktualizace, odstraňte soubor {0} ručně.</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>Požadavek selhal po {0} pokusech.</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -184,14 +180,19 @@ StackTrace:
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>Nelze zkontrolovat nejnovější verzi.</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>Aktualizaci nelze provést, protože neexistuje žádný asset související s aktuálně spuštěnou verzí. Automatickou aktualizaci této verze nelze provést.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>Aktualizace nemohla pokračovat, protože žádaná verze neobsahuje žádné assety.</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>Obdržen vstup od uživatele, ale proces běží v automatickém režimu.</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>Požadavek byl zamítnut, protože není nastaven identifikátor SteamOwnerID.</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>Ukončení...</value>
|
||||
</data>
|
||||
@@ -242,7 +243,8 @@ StackTrace:
|
||||
<value>Kontrola nové verze...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Probíhá stahování nové verze... Během čekání zvažte podpotu tohoto projektu! :)</value>
|
||||
<value>Probíhá stahování nové verze: {0} ({1} MB)...Během čekání zvažte podporu tohoto projektu! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Úspěšně aktualizováno.</value>
|
||||
@@ -282,7 +284,10 @@ StackTrace:
|
||||
<value>Prosím, zadejte nezdokumentovanou hodnotu {0}: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>Zadejte hostitele IPC:</value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>Obdržena neznámá konfigurace pro {0}, nahlašte: {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -291,10 +296,17 @@ StackTrace:
|
||||
<value>Spušt2ní více než {0} her není aktuálně možné, použito bude pouze prvních {0} položek z {1}.</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>Odpovězeno na příkaz IPC: {0} odpověď: {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>IPC server je připravený.</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>Spouštění serveru IPC na adrese {0}...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Tento bot byl již zastaven.</value>
|
||||
</data>
|
||||
@@ -546,10 +558,6 @@ StackTrace:
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Bot je právě použiván.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Nelze se připojit ke Steamu: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Nelze se příhlásit do Steamu: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -658,4 +666,8 @@ StackTrace:
|
||||
<value>Procházení fronty doporučení #{0} dokončeno.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>{0}/{1} botů již vlastní všechny uvedené hry.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -172,10 +172,6 @@ StackTrace:
|
||||
<value>Parsing {0} fejlet!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Kunne ikke fjerne gammel ASF binær fil. Vær venlig at fjerne {0} manuelt for at opdaterings funktionen virker!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>Anmodning fejlede efter {0} forsøg!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -240,9 +236,7 @@ StackTrace:
|
||||
<data name="UpdateCheckingNewVersion" xml:space="preserve">
|
||||
<value>Undersøger for ny version...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Downloader ny version... Mens du venter, overvej at donere hvis du sætter pris på det arbejde der bliver gjort! :)</value>
|
||||
</data>
|
||||
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Opdateringsprocessen er færdig!</value>
|
||||
</data>
|
||||
@@ -293,7 +287,6 @@ StackTrace:
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Denne bot er allerede stoppet!</value>
|
||||
</data>
|
||||
@@ -545,10 +538,6 @@ StackTrace:
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Botten bruges i øjeblikket.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Kan ikke forbinde til Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Kan ikke logge ind på Steam: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -657,4 +646,5 @@ StackTrace:
|
||||
<value>Færdig med rensning af Steam opdagelses kø #{0}.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
|
||||
</root>
|
||||
|
||||
@@ -172,10 +172,6 @@ StackTrace:
|
||||
<value>Fehler beim Parsen von {0}!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Konnte alte ASF Binärdatei nicht löschen. Bitte entferne {0} manuell, damit die Updatefunktion funktionieren kann!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>Anfrage nach {0} Versuchen fehlgeschlagen!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -183,14 +179,19 @@ StackTrace:
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>Neueste Version konnte nicht überprüft werden!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>Konnte mit der Aktualisierung nicht fortfahren, weil es keine Anlage gibt, welche sich auf die aktuell betriebene Version bezieht! Automatische Aktualisierung auf diese Version ist nicht möglich.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>Konnte nicht mit Aktualisierung fortfahren, weil diese Version keine Anlage enthält!</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>Anfrage für Benutzereingabe erhalten, aber der Prozess läuft im Headless-Modus!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>Verweigere die Bearbeitung dieser Anfrage, weil die SteamOwnerID nicht festgelegt wurde!</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>Vorgang wird beendet...</value>
|
||||
</data>
|
||||
@@ -241,7 +242,8 @@ StackTrace:
|
||||
<value>Prüfe auf neue Version...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Lade neue Version herunter... Während du wartest, denk darüber nach zu spenden, wenn du die geleistete Arbeit zu schätzen weißt! :)</value>
|
||||
<value>Lade neue Version herunter: {0} ({1} MB)... Während du wartest, denk darüber nach zu spenden, wenn du die geleistete Arbeit zu schätzen weißt! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Aktualisierung abgeschlossen!</value>
|
||||
@@ -281,7 +283,10 @@ StackTrace:
|
||||
<value>Bitte gebe undokumentierten Wert von {0} ein: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>Bitte gebe deinen IPC-Host ein: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>Unbekannten Wert für {0} erhalten, bitte melde folgendes: {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -290,10 +295,17 @@ StackTrace:
|
||||
<value>Das Spielen von mehr als {0} Spielen gleichzeitig ist nicht möglich, nur die ersten {0} Einträge von {1} werden verwendet!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>Auf IPC-Befehl geantwortet: {0} mit {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>ICP-Server bereit!</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>Starte IPC-Server auf {0}...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Dieser Bot hat bereits angehalten!</value>
|
||||
</data>
|
||||
@@ -545,10 +557,6 @@ StackTrace:
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Der Bot wird zurzeit benutzt.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Verbindung zu Steam nicht möglich: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Anmeldung in Steam nicht möglich: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -657,4 +665,8 @@ StackTrace:
|
||||
<value>Fertig mit Löschung der Steam Entdeckungsliste #{0}.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>Es gibt {0}/{1}-Bots, die bereits alle geprüften Spiele besitzen.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -173,10 +173,6 @@ StackTrace:
|
||||
<value>Fehler beim Parsen von {0}!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Konnte alte ASF Binärdatei nicht löschen. Bitte entferne {0} manuell, damit die Updatefunktion funktionieren kann!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>Anfrage nach {0} Versuchen fehlgeschlagen!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -184,14 +180,19 @@ StackTrace:
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>Konnte aktuellste Version nicht überprüfen!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>Konnte mit der Aktualisierung nicht fortfahren, weil es keine Anlage gibt, welche sich auf die aktuell betriebene Version bezieht! Automatische Aktualisierung auf diese Version ist nicht möglich.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>Konnte nicht mit Aktualisierung fortfahren, weil diese Version keine Anlage enthält!</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>Anfrage für Benutzereingabe erhalten, aber der Prozess läuft im Headless-Modus!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>Verweigere die Bearbeitung dieser Anfrage, weil die SteamOwnerID nicht festgelegt wurde!</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>Beende...</value>
|
||||
</data>
|
||||
@@ -242,7 +243,8 @@ StackTrace:
|
||||
<value>Prüfe auf neue Version...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Lade neue Version herunter... Während du wartest, denk darüber nach zu spenden, wenn du die geleistete Arbeit zu schätzen weißt! :)</value>
|
||||
<value>Lade neue Version herunter: {0} ({1} MB)... Während du wartest, denk darüber nach zu spenden, wenn du die geleistete Arbeit zu schätzen weißt! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Aktualisierung abgeschlossen!</value>
|
||||
@@ -282,7 +284,10 @@ StackTrace:
|
||||
<value>Bitte gebe undokumentierten Wert von {0} ein: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>Bitte gebe deinen IPC-Host ein: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>Unbekannten Wert für {0} erhalten, bitte melde folgendes: {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -291,10 +296,17 @@ StackTrace:
|
||||
<value>Das Spielen von mehr als {0} Spielen gleichzeitig ist nicht möglich, nur die ersten {0} Einträge von {1} werden verwendet!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>Auf IPC-Befehl geantwortet: {0} mit {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>ICP-Server bereit!</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>Starte IPC-Server auf {0}...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Dieser Bot hat bereits angehalten!</value>
|
||||
</data>
|
||||
@@ -546,10 +558,6 @@ StackTrace:
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Bot wird zurzeit benutzt.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Verbindung zu Steam nicht möglich: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Anmeldung in Steam nicht möglich: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -658,4 +666,8 @@ StackTrace:
|
||||
<value>Fertig mit Löschung der Steam Entdeckungsliste #{0}.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>Es gibt {0}/{1}-Bots, die bereits alle geprüften Spiele besitzen.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -134,7 +134,10 @@
|
||||
<value>Η ρυθμισμένη ιδιότητα {0} δεν είναι έγκυρη: {1}</value>
|
||||
<comment>{0} will be replaced by name of the configuration property, {1} will be replaced by invalid value</comment>
|
||||
</data>
|
||||
|
||||
<data name="ErrorEarlyFatalExceptionInfo" xml:space="preserve">
|
||||
<value>Το ASF V{0} αντιμετώπισε κρίσιμο σφάλμα πριν καν ξεκινήσει η κύρια μονάδα καταγραφής σφαλμάτων!</value>
|
||||
<comment>{0} will be replaced by version number</comment>
|
||||
</data>
|
||||
<data name="ErrorEarlyFatalExceptionPrint" xml:space="preserve">
|
||||
<value>Εξαίρεση: {0}() {1}
|
||||
StackTrace:
|
||||
@@ -166,10 +169,9 @@ StackTrace:
|
||||
<value>Το {0} είναι null!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Αδυναμία αφαίρεσης του παλιού ASF binary, αφαιρέστε το {0} χειροκίνητα ώστε να λειτουργήσει η λειτουργία ενημέρωσης!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
<data name="ErrorParsingObject" xml:space="preserve">
|
||||
<value>Η ανάλυση του {0} απέτυχε!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>Το αίτημα απέτυχε έπειτα από {0} προσπάθειες!</value>
|
||||
@@ -178,14 +180,19 @@ StackTrace:
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>Αδυναμία ελέγχου για την τελευταία έκδοση!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>Αδυναμία συνέχειας με την ενημέρωση καθώς δεν υπάρχουν τα απαραίτητα αρχεία που σχετίζονται με την τρέχουσα έκδοση. Η αυτόματη ενημέρωση σε αυτή την έκδοση δεν είναι δυνατή.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>Αδυναμία συνέχειας με την ενημέρωση γιατί η συγκεκριμένη έκδοση δεν περιέχει καθόλου αρχεία!</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>Λήφθηκε αίτημα για είσοδο από τον χρήστη, αλλά η διεργασία εκτελείται σε σιωπηλή λειτουργία!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>Άρνηση εκτέλεσης αυτού του αιτήματος επειδή το SteamOwnerID δεν έχει οριστεί!</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>Έξοδος...</value>
|
||||
</data>
|
||||
@@ -236,7 +243,8 @@ StackTrace:
|
||||
<value>Έλεγχος για νέα έκδοση...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Λήψη νέας έκδοσης... Όσο περιμένετε, σκεφτείτε να κάνετε μια δωρεά εάν εκτιμάτε τη δουλειά που γίνεται! :)</value>
|
||||
<value>Λήψη νέας έκδοσης: {0} ({1} MB)... Όσο περιμένετε, σκεφτείτε να κάνετε μια δωρεά εάν εκτιμάτε τη δουλειά που γίνεται! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Η διαδικασία ενημέρωσης ολοκληρώθηκε!</value>
|
||||
@@ -276,7 +284,10 @@ StackTrace:
|
||||
<value>Εισάγετε τη μη τεκμηριωμένη τιμή {0}: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>Παρακαλούμε εισάγετε τον διακομιστή IPC σας: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>Λήφθηκε άγνωστη τιμή για το {0}, παρακαλούμε αναφέρετέ το: {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -285,10 +296,17 @@ StackTrace:
|
||||
<value>Η συλλογή περισσότερων από {0} παιχνιδιών ταυτόχρονα δεν είναι δυνατή, μόνο οι πρώτες {0} καταχρήσεις από το {1} θα χρησιμοποιηθούν!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>Έγινε απάντηση στην εντολή IPC: {0} με: {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>Ο διακομιστής IPC είναι έτοιμος!</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>Έναρξη διακομιστή IPC στο {0}...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Το bot έχει ήδη σταματήσει!</value>
|
||||
</data>
|
||||
@@ -449,7 +467,10 @@ StackTrace:
|
||||
<data name="BotInstanceNotStartingBecauseDisabled" xml:space="preserve">
|
||||
<value>Δεν γίνεται εκκίνηση αυτού του bot καθώς έχει απενεργοποιηθεί στο αρχείο διαμόρφωσης!</value>
|
||||
</data>
|
||||
|
||||
<data name="BotInvalidAuthenticatorDuringLogin" xml:space="preserve">
|
||||
<value>Λήφθηκε κωδικός σφάλματος «TwoFactorCodeMismatch» {0} συνεχόμενες φορές. Αυτό σχεδόν πάντα υποδεικνύει μη έγκυρα στοιχεία εισόδου στο ASF 2FA. Γίνεται τερματισμός!</value>
|
||||
<comment>{0} will be replaced by maximum allowed number of failed 2FA attempts</comment>
|
||||
</data>
|
||||
<data name="BotLoggedOff" xml:space="preserve">
|
||||
<value>Έγινε αποσύνδεση από το Steam: {0}</value>
|
||||
<comment>{0} will be replaced by logging off reason (string)</comment>
|
||||
@@ -460,20 +481,36 @@ StackTrace:
|
||||
<data name="BotLoggingIn" xml:space="preserve">
|
||||
<value>Σύνδεση...</value>
|
||||
</data>
|
||||
|
||||
<data name="BotLogonSessionReplaced" xml:space="preserve">
|
||||
<value>Αυτός ο λογαριασμός φαίνεται να χρησιμοποιείται σε άλλη συνεδρία ASF, το οποίο προκαλεί απρόσμενη συμπεριφορά. Θα γίνει τερματισμός!</value>
|
||||
</data>
|
||||
<data name="BotLootingFailed" xml:space="preserve">
|
||||
<value>Η πρόταση ανταλλαγής απέτυχε!</value>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="BotLootingMasterNotDefined" xml:space="preserve">
|
||||
<value>Η ανταλλαγή δεν μπορεί να αποσταλεί καθώς δεν υπάρχει κανένας χρήστης με ορισμένο το δικαίωμα «master»!</value>
|
||||
</data>
|
||||
<data name="BotLootingNoLootableTypes" xml:space="preserve">
|
||||
<value>Δεν έχετε ορίσει τύπους αντικειμένων για μαζική συλλογή!</value>
|
||||
</data>
|
||||
<data name="BotLootingNowDisabled" xml:space="preserve">
|
||||
<value>Η μαζική συλλογή απενεργοποιήθηκε!</value>
|
||||
</data>
|
||||
<data name="BotLootingNowEnabled" xml:space="preserve">
|
||||
<value>Η μαζική συλλογή ενεργοποιήθηκε!</value>
|
||||
</data>
|
||||
<data name="BotLootingSuccess" xml:space="preserve">
|
||||
<value>Η πρόταση ανταλλαγής στάλθηκε επιτυχώς!</value>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
<data name="BotLootingTemporarilyDisabled" xml:space="preserve">
|
||||
<value>Η μαζική συλλογή είναι προσωρινά απενεργοποιημένη!</value>
|
||||
</data>
|
||||
<data name="BotLootingYourself" xml:space="preserve">
|
||||
<value>Δεν μπορείτε να κάνετε μαζική συλλογή από τον εαυτό σας!</value>
|
||||
</data>
|
||||
<data name="BotNoASFAuthenticator" xml:space="preserve">
|
||||
<value>Αυτό το bot δεν έχει ενεργοποιημένο το ASF 2FA! Μήπως ξεχάσατε να εισάγετε τον επαληθευτή σας ως ASF 2FA;</value>
|
||||
</data>
|
||||
<data name="BotNotConnected" xml:space="preserve">
|
||||
<value>Αυτό το bot δεν έχει συνδεθεί!</value>
|
||||
</data>
|
||||
@@ -521,10 +558,6 @@ StackTrace:
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Το bot χρησιμοποιείται αυτή τη στιγμή.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Αδυναμία σύνδεσης στο Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Αδυναμία σύνδεσης στο Steam: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -633,4 +666,8 @@ StackTrace:
|
||||
<value>Ολοκληρώθηκε η εκκαθάριση σειράς ανακαλύψεων Steam #{0}.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>Υπάρχουν {0}/{1} bot που κατέχουν ήδη όλα τα παιχνίδια για τα οποία γίνεται έλεγχος.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
<comment>{0} will be replaced by content string. Please note that this string should include newline for formatting.</comment>
|
||||
</data>
|
||||
<data name="ErrorConfigPropertyInvalid" xml:space="preserve">
|
||||
<value>La propiedad {0} configurada es inválida: {1}</value>
|
||||
<value>La propiedad {0} configurada no es válida: {1}</value>
|
||||
<comment>{0} will be replaced by name of the configuration property, {1} will be replaced by invalid value</comment>
|
||||
</data>
|
||||
<data name="ErrorEarlyFatalExceptionInfo" xml:space="preserve">
|
||||
@@ -139,7 +139,7 @@
|
||||
</data>
|
||||
<data name="ErrorEarlyFatalExceptionPrint" xml:space="preserve">
|
||||
<value>Excepción: {0}() {1}
|
||||
Trazo de pila:
|
||||
StackTrace:
|
||||
{2}</value>
|
||||
<comment>{0} will be replaced by function name, {1} will be replaced by exception message, {2} will be replaced by entire stack trace. Please note that this string should include newlines for formatting.</comment>
|
||||
</data>
|
||||
@@ -155,7 +155,7 @@ Trazo de pila:
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorIsInvalid" xml:space="preserve">
|
||||
<value>{0} es inválido!</value>
|
||||
<value>¡{0} no es válido!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorMobileAuthenticatorInvalidDeviceID" xml:space="preserve">
|
||||
@@ -172,10 +172,6 @@ Trazo de pila:
|
||||
<value>Análisis de {0} fallido!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>No se pudo borrar el anterior ejecutable de ASF. Por favor elimina {0} manualmente para que la actualización funcione!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>¡La solicitud falló después de {0} intentos!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -183,16 +179,21 @@ Trazo de pila:
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>¡No se pudo comprobar la última versión!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>No se ha podido proceder con la actualización porque no hay ninguna opcion que se relacione con la version actual! La actualización automatica a esa version no es posible.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>¡No se puede continuar con una actualización porque esa versión no incluye ningún recurso!</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>Recibida una solicitud de entrada del usuario, ¡pero el proceso se está ejecutando en modo servidor!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>¡la solicitud no se puede atender porque SteamOwnerID no esta establecido!</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>Saliendo...</value>
|
||||
<value>Terminando...</value>
|
||||
</data>
|
||||
<data name="WarningFailed" xml:space="preserve">
|
||||
<value>¡Fallido!</value>
|
||||
@@ -212,7 +213,7 @@ Trazo de pila:
|
||||
<comment>{0} will be replaced by service's name</comment>
|
||||
</data>
|
||||
<data name="NoBotsAreRunning" xml:space="preserve">
|
||||
<value>No hay bots activos, saliendo...</value>
|
||||
<value>No hay bots activos, terminando...</value>
|
||||
</data>
|
||||
<data name="RefreshingOurSession" xml:space="preserve">
|
||||
<value>¡Actualizando sesión!</value>
|
||||
@@ -241,7 +242,8 @@ Trazo de pila:
|
||||
<value>Comprobando si existe una nueva versión...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Descargando la nueva versión... ¡Mientras espera, considere donar si aprecia el trabajo que se está realizando! :)</value>
|
||||
<value>Descargando la nueva versión: {0} ({1} MB)... ¡Mientras espera, considere donar si aprecia el trabajo que se está realizando! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>¡Proceso de actualización finalizado!</value>
|
||||
@@ -266,7 +268,7 @@ Trazo de pila:
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="UserInputSteamLogin" xml:space="preserve">
|
||||
<value>Por favor ingresa tus credenciales de Steam: </value>
|
||||
<value>Introduzca su usuario de Steam: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="UserInputSteamParentalPIN" xml:space="preserve">
|
||||
@@ -281,7 +283,10 @@ Trazo de pila:
|
||||
<value>Por favor ingresa el valor indocumentado de {0}: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>Por favor ingresa tu host WFC: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>Recibido valor desconocido para {0}. Por favor, informa de ello: {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -290,10 +295,17 @@ Trazo de pila:
|
||||
<value>¡Ejecutar más de {0} juegos a la vez no es posible, sólo se usarán las primeras {0} entradas de {1}!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>Se ha respondido al comando WCF: {0} con: {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>¡Servidor WCF listo!</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>Iniciando servidor WCF en {0}...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>¡Este bot ya se ha detenido!</value>
|
||||
</data>
|
||||
@@ -413,7 +425,7 @@ Trazo de pila:
|
||||
<value>Convirtiendo .maFile en formato ASF...</value>
|
||||
</data>
|
||||
<data name="BotAuthenticatorImportFinished" xml:space="preserve">
|
||||
<value>Autenticador móvil importado exitosamente.</value>
|
||||
<value>¡Se ha finalizado exitosamente la importación del identificador móvil!</value>
|
||||
</data>
|
||||
<data name="BotAuthenticatorInvalidDeviceID" xml:space="preserve">
|
||||
<value>¡Su DeviceID es incorrecto o no existe!</value>
|
||||
@@ -545,10 +557,6 @@ Trazo de pila:
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>El bot se está utilizando actualmente.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>No se puede conectar a Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>No se puede iniciar sesión en Steam: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -649,6 +657,16 @@ Trazo de pila:
|
||||
<value>Uso de memoria actual: {0} MB.</value>
|
||||
<comment>{0} will be replaced by number (in megabytes) of memory being used</comment>
|
||||
</data>
|
||||
|
||||
|
||||
<data name="ClearingDiscoveryQueue" xml:space="preserve">
|
||||
<value>Haciendo la lista de descubrimientos de Steam #{0}...</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="DoneClearingDiscoveryQueue" xml:space="preserve">
|
||||
<value>Lista de decubrimientos de Steam #{0} completada.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>Hay {0}/{1} bots que poseen todos los juegos comprobados.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -152,10 +152,6 @@
|
||||
<value>{0} jäsentäminen epäonnistui!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Vanhaa ASF-binaariä ei voitu poistaa. Ole hyvä ja poista {0} manuaalisesti, jotta päivitystoiminto toimisi!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>Pyyntö epäonnistui {0} yrityksen jälkeen!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -201,9 +197,7 @@
|
||||
<data name="UpdateCheckingNewVersion" xml:space="preserve">
|
||||
<value>Tarkistetaan päivityksiä...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Ladataan päivityksiä... Odottaessa, harkitse lahjoittamista kiittääksesi työhön käytetystä ajasta! :)</value>
|
||||
</data>
|
||||
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Päivitys valmis!</value>
|
||||
</data>
|
||||
@@ -228,7 +222,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="BotNotFound" xml:space="preserve">
|
||||
<value>Bottia nimeltä {0} ei voitu löytää!</value>
|
||||
<comment>{0} will be replaced by bot's name query (string)</comment>
|
||||
@@ -370,7 +363,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="ErrorIsEmpty" xml:space="preserve">
|
||||
<value>{0} on tyhjä!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
@@ -416,4 +408,5 @@
|
||||
|
||||
|
||||
|
||||
|
||||
</root>
|
||||
|
||||
@@ -173,10 +173,6 @@ StackTrace :
|
||||
<value>L’analyse de {0} a échoué !</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Impossible de supprimer l'ancien binaire d'ASF, merci de supprimer {0} manuellement afin que la fonction de mise à jour puisse fonctionner !</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>La requête a échoué après {0} tentatives !</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -184,14 +180,19 @@ StackTrace :
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>Impossible de vérifier la dernière version !</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>Impossible de procéder à la mise à jour car il n'y a aucun fichier correspondant à la version actuelle ! La mise à jour automatique vers cette version n'est pas possible.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>Impossible de procéder à une mise à jour parce que cette version ne contient aucun fichier !</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>Réception d'une demande d'entrée utilisateur, mais le processus en cours tourne en mode non-interactif!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>Refus de traiter la requête car le paramètre SteamOwnerID n’est pas défini !</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>Fermeture...</value>
|
||||
</data>
|
||||
@@ -242,7 +243,8 @@ StackTrace :
|
||||
<value>Recherche d'une nouvelle version...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Téléchargement de la nouvelle version en cours... En attendant, vous pouvez faire un don si vous appréciez le travail effectué ! :)</value>
|
||||
<value>Téléchargement de la nouvelle version en cours: {0} ({1} MB)... En attendant, envisagez de faire un don si vous appréciez le travail effectué ! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Mise à jour terminée !</value>
|
||||
@@ -282,7 +284,10 @@ StackTrace :
|
||||
<value>Veuillez entrer la valeur non documentée de {0} : </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>Veuillez saisir votre hôte IPC: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>{0} a reçu une valeur inconnue, veuillez le signaler : {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -291,10 +296,17 @@ StackTrace :
|
||||
<value>Jouer à plus de {0} jeux en même temps n’est pas actuelllement possible, seules les {0} premières entrées de {1} seront utilisées !</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>Réponse à la commande IPC: {0} avec {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>Serveur IPC prêt !</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>Démarrage du serveur IPC sur {0}...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Ce bot est déjà arrêté !</value>
|
||||
</data>
|
||||
@@ -546,10 +558,6 @@ StackTrace :
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Le bot est actuellement utilisé.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Impossible de se connecter à Steam : {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Impossible de se connecter à Steam : {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -650,6 +658,16 @@ StackTrace :
|
||||
<value>Mémoire actuellement utilisée : {0} MB.</value>
|
||||
<comment>{0} will be replaced by number (in megabytes) of memory being used</comment>
|
||||
</data>
|
||||
|
||||
|
||||
<data name="ClearingDiscoveryQueue" xml:space="preserve">
|
||||
<value>Consultation de la liste de découvertes en cours #{0}...</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="DoneClearingDiscoveryQueue" xml:space="preserve">
|
||||
<value>Fini de consulter la liste de découvertes #{0}.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>Il y a {0}/{1} bots qui possède déjà tous les jeux en cours de vérification.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -173,10 +173,6 @@ StackTrace :
|
||||
<value>L’analyse de {0} a échoué !</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Impossible de supprimer l'ancien fichier d'ASF. Merci de supprimer {0} manuellement afin que la fonction de mise à jour puisse fonctionner !</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>La requête a échoué après {0} tentatives !</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -184,14 +180,19 @@ StackTrace :
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>Impossible de vérifier la dernière version !</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>Impossible de procéder à la mise à jour car il n'y a aucun fichier correspondant à la version actuelle ! La mise à jour automatique vers cette version n'est pas possible.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>Impossible de procéder à une mise à jour parce que cette version ne contient aucun fichier !</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>Réception d'une demande d'entrée utilisateur, mais le processus en cours tourne en mode non-interactif !</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>Refus de traiter la requête car le paramètre SteamOwnerID n’est pas défini !</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>Fermeture...</value>
|
||||
</data>
|
||||
@@ -242,7 +243,8 @@ StackTrace :
|
||||
<value>Recherche d'une nouvelle version...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Téléchargement de la nouvelle version en cours... En attendant, vous pouvez faire un don si vous appréciez le travail effectué ! :)</value>
|
||||
<value>Téléchargement de la nouvelle version en cours: {0} ({1} MB)... En attendant, envisagez de faire un don si vous appréciez le travail effectué ! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Mise à jour terminée !</value>
|
||||
@@ -251,7 +253,7 @@ StackTrace :
|
||||
<value>Une nouvelle version d'ASF est disponible ! Envisagez de la mettre à jour !</value>
|
||||
</data>
|
||||
<data name="UpdateVersionInfo" xml:space="preserve">
|
||||
<value>Version installée : {0} | Version la plus récente : {1}</value>
|
||||
<value>Version locale : {0} | Version la plus récente : {1}</value>
|
||||
<comment>{0} will be replaced by current version, {1} will be replaced by remote version</comment>
|
||||
</data>
|
||||
<data name="UserInputDeviceID" xml:space="preserve">
|
||||
@@ -282,7 +284,10 @@ StackTrace :
|
||||
<value>Veuillez entrer la valeur non documentée de {0} : </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>Veuillez saisir votre hôte IPC: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>{0} a reçu une valeur inconnue, veuillez le signaler : {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -291,10 +296,17 @@ StackTrace :
|
||||
<value>Jouer à plus de {0} jeux en même temps n’est pas possible, seules les {0} premières entrées de {1} seront utilisées !</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>Réponse à la commande IPC: {0} avec {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>Serveur IPC prêt !</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>Démarrage du serveur IPC sur {0}...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Ce bot est déjà à l'arrêt !</value>
|
||||
</data>
|
||||
@@ -546,10 +558,6 @@ StackTrace :
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Le bot est actuellement utilisé.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Impossible de se connecter à Steam : {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Impossible de se connecter à Steam : {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -650,6 +658,16 @@ StackTrace :
|
||||
<value>Mémoire actuellement utilisée : {0} MB.</value>
|
||||
<comment>{0} will be replaced by number (in megabytes) of memory being used</comment>
|
||||
</data>
|
||||
|
||||
|
||||
<data name="ClearingDiscoveryQueue" xml:space="preserve">
|
||||
<value>Consultation de la liste de découvertes en cours #{0}...</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="DoneClearingDiscoveryQueue" xml:space="preserve">
|
||||
<value>Fini de consulter la liste de découvertes #{0}.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>Il y a {0}/{1} bots qui possède déjà tous les jeux en cours de vérification.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -167,7 +167,6 @@ StackTrace:
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
|
||||
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>לא ניתן לבדוק את הגירסה העדכנית ביותר!</value>
|
||||
</data>
|
||||
@@ -228,9 +227,7 @@ StackTrace:
|
||||
<data name="UpdateCheckingNewVersion" xml:space="preserve">
|
||||
<value>מחפש גירסה חדשה...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>מוריד גירסה חדשה... בזמן ההמתנה, אנא שקלו לתרום אם אתם מעריכים את העבודה שאנו עושים :)</value>
|
||||
</data>
|
||||
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>תהליך העדכון הסתיים!</value>
|
||||
</data>
|
||||
@@ -255,7 +252,6 @@ StackTrace:
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="BotNotFound" xml:space="preserve">
|
||||
<value>לא ניתן למצוא אף בוט בשם {0}!</value>
|
||||
<comment>{0} will be replaced by bot's name query (string)</comment>
|
||||
@@ -436,10 +432,6 @@ StackTrace:
|
||||
|
||||
|
||||
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>לא ניתן להתחבר לסטים: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>לא ניתן להתחבר לסטים: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -490,4 +482,5 @@ StackTrace:
|
||||
|
||||
|
||||
|
||||
|
||||
</root>
|
||||
|
||||
@@ -163,7 +163,6 @@
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
|
||||
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>कुड नॉट चैक लैटेस्ट वर्ज़न!</value>
|
||||
</data>
|
||||
@@ -309,7 +308,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</root>
|
||||
|
||||
@@ -171,10 +171,6 @@ StackTrace: {2}</value>
|
||||
<value>{0} feldolgozása sikertelen!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Nem lehet kitörölni a régi ASF bináris fájlt. Kérlek manuálisan távolítsd el a {0}-t, hogy a frissítés sikeres legyen!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>A kérés sikertelen volt {0} próbálkozás után!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -239,9 +235,7 @@ StackTrace: {2}</value>
|
||||
<data name="UpdateCheckingNewVersion" xml:space="preserve">
|
||||
<value>Új verzió keresése...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Új verzió letöltése folyamatban... Mialatt várakozol, fontold meg, hogy pénzzel támogatod a munkámat, ha tetszik, amit csinálok! :)</value>
|
||||
</data>
|
||||
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Frissítés kész!</value>
|
||||
</data>
|
||||
@@ -292,7 +286,6 @@ StackTrace: {2}</value>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Ez a bor már leállt!</value>
|
||||
</data>
|
||||
@@ -544,10 +537,6 @@ StackTrace: {2}</value>
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>A bot jelenleg használatban van.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Nem lehet csatlakozni a Steamhez: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Nem lehet belépni a Steamre: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -656,4 +645,5 @@ StackTrace: {2}</value>
|
||||
<value>{0}-s számú Steam Felfedezési Várólista kitsztítva.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
|
||||
</root>
|
||||
|
||||
@@ -130,8 +130,7 @@
|
||||
<comment>{0} will be replaced by content string. Please note that this string should include newline for formatting.</comment>
|
||||
</data>
|
||||
<data name="ErrorConfigPropertyInvalid" xml:space="preserve">
|
||||
<value>ErrorPropertiConfiginvalid
|
||||
{0} akan diubah dengan nama properti konfigurasi, {1} akan diubah dengan nilai invalid</value>
|
||||
<value>Konfigurasi properti {0} tidak valid: {1}</value>
|
||||
<comment>{0} will be replaced by name of the configuration property, {1} will be replaced by invalid value</comment>
|
||||
</data>
|
||||
<data name="ErrorEarlyFatalExceptionInfo" xml:space="preserve">
|
||||
@@ -171,10 +170,6 @@
|
||||
<value>Parsing {0} gagal!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Tidak dapat menghapus file biner ASF lama, silakan hapus {0} secara manual agar fungsi update bekerja!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>Permintaan gagal setelah {0} kali upaya!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -182,14 +177,19 @@
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>Tidak dapat memeriksa versi terbaru!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>Tidak bisa melanjutkan dengan pembaruan karena tidak ada asset yang berkaitan dengan versi yang berjalan sekarang! Pembaruan otomatis ke versi tersebut tidak mungkin.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>Tidak bisa melanjutkan update karena tak ada aset yang termasuk dalam versi tersebut!</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>Menerima permintaan untuk input pengguna, tetapi proses berjalan dalam mode Headless!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>Menolak untuk menangani permintaan karena SteamOwnerID tidak diatur!</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>Menutup...</value>
|
||||
</data>
|
||||
@@ -240,10 +240,11 @@
|
||||
<value>Sedang mengecek versi terbaru...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Mengunduh versi baru... Sambil menunggu, pertimbangkan untuk mengapresiasi seluruh kerja keras dengan mendonasi! :)</value>
|
||||
<value>Mengunduh versi baru: {0} ({1} MB)... Sambil menunggu, pertimbangkan untuk mengapresiasi seluruh kerja keras dengan mendonasi! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Proses update selesai!</value>
|
||||
<value>Proses pembaruan selesai!</value>
|
||||
</data>
|
||||
<data name="UpdateNewVersionAvailable" xml:space="preserve">
|
||||
<value>Versi terbaru ASF tersedia! Pertimbangkan untuk di-update!</value>
|
||||
@@ -280,19 +281,29 @@
|
||||
<value>Masukkan suatu nilai yang tidak terdokumentasi {0}: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>Masukkan host WCF Anda: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>Menerima nilai yang tidak diketahui untuk {0}, laporkan hal ini: {1}</value>
|
||||
<value>Menerima nilai yang tidak diketahui untuk {0}, mohon laporkan ini: {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
</data>
|
||||
<data name="WarningTooManyGamesToPlay" xml:space="preserve">
|
||||
<value>Tidak dapat bermain lebih dari {0} game secara bersamaan, hanya {0} entri pertama dari {1} game yang akan digunakan</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>Jawaban untuk perintah IPC: {0} dengan: {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>Server IPC siap!</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>Memulai server IPC di {0}...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Bot ini sudah berhenti!</value>
|
||||
</data>
|
||||
@@ -319,14 +330,14 @@
|
||||
<value>Mengecek halaman badge lainnya...</value>
|
||||
</data>
|
||||
<data name="ChosenFarmingAlgorithm" xml:space="preserve">
|
||||
<value>Memilih Algoritma Idling: {0}</value>
|
||||
<value>Memilih algoritma idling: {0}</value>
|
||||
<comment>{0} will be replaced by the name of chosen idling algorithm</comment>
|
||||
</data>
|
||||
<data name="Done" xml:space="preserve">
|
||||
<value>Selesai!</value>
|
||||
</data>
|
||||
<data name="GamesToIdle" xml:space="preserve">
|
||||
<value>Kami memiliki total {0} permainan ({1} kartu) meninggalkan ke siaga (~{2} yang tersisa)...</value>
|
||||
<value>Kita memiliki total {0} permainan ({1} kartu) tersisa untuk idle (~{2} tersisa)...</value>
|
||||
<comment>{0} will be replaced by number of games, {1} will be replaced by number of cards, {2} will be replaced by translated TimeSpan string (such as "1 day, 5 hours and 30 minutes")</comment>
|
||||
</data>
|
||||
<data name="IdlingFinished" xml:space="preserve">
|
||||
@@ -362,7 +373,7 @@
|
||||
<comment>{0} will be replaced by list of the games (IDs, numbers), separated by a comma</comment>
|
||||
</data>
|
||||
<data name="PlayingNotAvailable" xml:space="preserve">
|
||||
<value>Bermain sedang tidak tersedia, kami akan mencoba lagi nanti!</value>
|
||||
<value>Bermain sedang tidak tersedia, kita akan coba lagi nanti!</value>
|
||||
</data>
|
||||
<data name="StillIdling" xml:space="preserve">
|
||||
<value>Masih idling: {0} ({1})</value>
|
||||
@@ -462,7 +473,7 @@
|
||||
<comment>{0} will be replaced by logging off reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotLoggedOn" xml:space="preserve">
|
||||
<value>Berhasil Login!</value>
|
||||
<value>Berhasil login!</value>
|
||||
</data>
|
||||
<data name="BotLoggingIn" xml:space="preserve">
|
||||
<value>Sedang masuk...</value>
|
||||
@@ -544,10 +555,6 @@
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Bot saat ini sedang digunakan.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Tidak dapat terhubung ke Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Tidak dapat login ke Steam: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -656,4 +663,8 @@
|
||||
<value>Selesai membersihkan antrian penemuan Steam #{0}.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>Ada {0}/{1} bot yang sudah memiliki semua permainan yang sedang diperiksa.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -171,10 +171,6 @@
|
||||
<value>Analisi di {0} non riuscita!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Impossibile rimuovere i vecchi file binari di ASF. Si prega di rimuovere {0} manualmente affinché l'aggiornamento funzioni correttamente!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>Richiesta non riuscita dopo {0} tentativi!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -182,14 +178,19 @@
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>Non è stato possibile controllare la versione più recente!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>Non posso procedere con l'aggiornamento perché non c'è una risorsa legata alla versione in esecuzione al momento! Un aggiornamento automatico a quella versione non è possibile.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>Impossibile procedere con un aggiornamento poiché tale versione non include risorse!</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>Ricevuta una richiesta di input da parte dell'utente, ma il processo è in esecuzione in modalità headless!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>Rifiutando di gestire la richiesta poiché SteamOwnerID non è stato impostato correttamente!</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>Uscita in corso...</value>
|
||||
</data>
|
||||
@@ -240,7 +241,8 @@
|
||||
<value>Verifica della nuova versione...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Scaricando la nuova versione... Durante l'attesa, considera una donazione se apprezzi il lavoro svolto! :)</value>
|
||||
<value>Scaricando la nuova versione: {0} ({1} MB)... Durante l'attesa, considera una donazione se apprezzi il lavoro svolto! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Aggiornamento completato!</value>
|
||||
@@ -269,7 +271,7 @@
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="UserInputSteamParentalPIN" xml:space="preserve">
|
||||
<value>Si prega di inserire il PIN famigliare Steam: </value>
|
||||
<value>Si prega di inserire il PIN famigliare di Steam: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="UserInputSteamPassword" xml:space="preserve">
|
||||
@@ -280,7 +282,10 @@
|
||||
<value>Inserisci il valore non documentato {0}: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>Inserisci il tuo host IPC: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>Ricevuto valore sconosciuto per {0}, si prega di segnalare: {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -289,10 +294,17 @@
|
||||
<value>Non è possibile giocare a più di {0} giochi contemporaneamente, verranno utilizzate solo le prime {0} voci di {1}!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>Risposto al comando IPC: {0} con: {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>Il server IPC è pronto!</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>Avvio del server IPC in {0}...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Questo bot è già stato arrestato!</value>
|
||||
</data>
|
||||
@@ -435,7 +447,7 @@
|
||||
<comment>{0} will be replaced by translated TimeSpan string (such as "5 minutes")</comment>
|
||||
</data>
|
||||
<data name="BotAutomaticIdlingResumedAlready" xml:space="preserve">
|
||||
<value>L'idling automatico è già attivo!</value>
|
||||
<value>L'idling automatico è già stato ripreso!</value>
|
||||
</data>
|
||||
<data name="BotConnected" xml:space="preserve">
|
||||
<value>Connesso a Steam!</value>
|
||||
@@ -527,7 +539,7 @@
|
||||
<value>Rimossa la chiave di accesso scaduta!</value>
|
||||
</data>
|
||||
<data name="BotStatusNotIdling" xml:space="preserve">
|
||||
<value>Il Bot non sta trovando niente.</value>
|
||||
<value>Il Bot non sta trovando niente su cui fare idling.</value>
|
||||
</data>
|
||||
<data name="BotStatusLimited" xml:space="preserve">
|
||||
<value>Il Bot è un account limitato e non può ottenere carte tramite idling.</value>
|
||||
@@ -536,7 +548,7 @@
|
||||
<value>Il bot si stà connettendo alla rete di Steam.</value>
|
||||
</data>
|
||||
<data name="BotStatusNotRunning" xml:space="preserve">
|
||||
<value>Bot non è in esecuzione.</value>
|
||||
<value>Il bot non è in esecuzione.</value>
|
||||
</data>
|
||||
<data name="BotStatusPaused" xml:space="preserve">
|
||||
<value>Il bot è in pausa o in esecuzione in modalità manuale.</value>
|
||||
@@ -544,10 +556,6 @@
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Il bot è attualmente in uso.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Impossibile connettersi a Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Impossibile effettuare l'accesso a Steam: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -614,7 +622,7 @@
|
||||
<comment>{0} will be replaced by culture code, such as "en-US", {1} will be replaced by completeness percentage, such as "78.5%"</comment>
|
||||
</data>
|
||||
<data name="IdlingGameNotPossible" xml:space="preserve">
|
||||
<value>L'idling di {0} ({1}) è temporaneamente disabilitato, poiché ASF non è in grado di riprodurre questo gioco al momento.</value>
|
||||
<value>L'idling di {0} ({1}) è temporaneamente disabilitato, poiché ASF non è in grado di eseguire questo gioco al momento.</value>
|
||||
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
|
||||
</data>
|
||||
<data name="WarningIdlingGameMismatch" xml:space="preserve">
|
||||
@@ -656,4 +664,8 @@
|
||||
<value>Fine coda #{0} dell'elenco scoperte Steam.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>Ci sono {0}/{1} bots che posseggono già tutti i giochi che sono stati controllati.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -170,10 +170,6 @@
|
||||
<value>{0} の解析に失敗しました!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>古いASFのバイナリを削除できませんでした。アップデート機能を動作させるには、{0} を手動で削除してください!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>要求を{0} 回試行し、失敗しました!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -188,7 +184,10 @@
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>ユーザー入力のリクエストを受け取りましたが、プロセスはheadlessモードで実行されています!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>SteamOwnerIDが設定されていないため、要求を処理しませんでした!</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>終了中...</value>
|
||||
</data>
|
||||
@@ -239,7 +238,8 @@
|
||||
<value>新しいバージョンをチェックしています...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>新しいバージョンをダウンロードしています... 待っている間、作者への寄付をご検討ください! :)</value>
|
||||
<value>新しいバージョンをダウンロードしています: {0} ({1} MB) ... 待っている間、作者への寄付をご検討ください! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>アップデート完了!</value>
|
||||
@@ -279,7 +279,10 @@
|
||||
<value>{0} の文章化されていない値を入力してください: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>あなたのIPCホストを入力してください: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>{0} に不明な値を受信しました。この問題を報告してください: {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -288,10 +291,17 @@
|
||||
<value>同時に{0} つより多くのゲームをプレイすることはできません。{1} から最初の{0} だけが使用されます!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>IPC コマンド: {0} 返答:{1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>IPC サーバーの準備ができました!</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>IPC サーバーを {0} に開始中...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>このBotは既に停止しています!</value>
|
||||
</data>
|
||||
@@ -543,10 +553,6 @@
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Botは現在使用されています。</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Steamに接続できませんでした: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Steamにログインできませんでした: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -649,4 +655,5 @@
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
</root>
|
||||
|
||||
@@ -173,10 +173,6 @@ StackTrace:
|
||||
<value>{0}의 분석에 실패했습니다!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>이전의 ASF 실행 파일을 제거할 수 없습니다. 업데이트 기능의 작동을 위해, 수동으로 {0}을(를) 제거하시기 바랍니다.</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>{0} 번의 시도 후, 요청이 실패했습니다!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -184,14 +180,19 @@ StackTrace:
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>최신 버전을 확인할 수 없습니다!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>현재 실행 중인 버전과 관련된 자산이 없으므로 업데이트를 진행할 수 없습니다! 해당 버전으로 자동 업데이트가 불가능합니다.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>해당 버전이 아무 내용도 포함되어 있지 않아 업데이트를 진행할 수 없습니다!</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>사용자 입력 요청을 받았지만, 프로세스는 Headless 모드로 실행 중입니다.</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>SteamOwnerID가 설정되지 않았기 때문에 요청을 거절합니다!</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>종료 중...</value>
|
||||
</data>
|
||||
@@ -242,7 +243,8 @@ StackTrace:
|
||||
<value>새로운 버전 확인 중...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>새로운 버전을 다운로드 중... 기다리는 동안, 완성된 작업이 고맙다면 기부를 고려해 보세요! :)</value>
|
||||
<value>새로운 버전을 내려받는 중: {0} ({1} MB).... 기다리는 동안, 이 프로그램이 고맙다면, 기부하는 것을 고려해보세요! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>업데이트 작업 완료!</value>
|
||||
@@ -282,7 +284,10 @@ StackTrace:
|
||||
<value>등록되지 않은 {0}의 값을 입력하세요: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>IPC 호스트를 입력하세요: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>{0}의 알 수 없는 값을 받았습니다. 이것을 보고 바랍니다: {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -291,10 +296,17 @@ StackTrace:
|
||||
<value>동시에 {0}개 이상의 게임들을 플레이하는 것은 불가능합니다. {1}에 의해 단지 {0}개의 항목만 사용될 것입니다!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>{0} IPC 명령에 대한 응답: {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>IPC 서버 준비 완료!</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>{0} 에서 IPC 서버 시작...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>이 봇은 이미 중지되어 있습니다!</value>
|
||||
</data>
|
||||
@@ -546,10 +558,6 @@ StackTrace:
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>봇 - 현재 사용 중.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Steam에 연결할 수 없습니다: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Steam에 로그인할 수 없습니다: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -658,4 +666,8 @@ StackTrace:
|
||||
<value>스팀 맞춤 대기열 #{0}을 지웠습니다.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>게임을 확인 중인 {0}/{1} 개의 봇이 있습니다.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -149,7 +149,7 @@
|
||||
<comment>{0} will be replaced by URL of the request</comment>
|
||||
</data>
|
||||
<data name="ErrorGlobalConfigNotLoaded" xml:space="preserve">
|
||||
<value>Pasaulinė konfigūracija negalėjo būti įkelta, įsitikinkite, kad {0} egzistuoja ir yra galiojantis! Sekite steigimo vadovą vikipedijoje jei esate supainioti.</value>
|
||||
<value>Pagrindinė konfigūracija negalėjo būti įkelta, įsitikinkite, kad {0} egzistuoja ir yra galiojantis! Sekite steigimo vadovą vikipedijoje jei esate supainioti.</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorIsInvalid" xml:space="preserve">
|
||||
@@ -170,10 +170,6 @@
|
||||
<value>Apdorojanti {0} nepavyko!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Nepavyko pašalinti senos ASF bibliotekos. Prašome pašalinti {0} rankiniu būdu tam, kad atnaujinimo funkcija veiktų!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>Užklausa nepavyko po {0} bandymų!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -181,14 +177,19 @@
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>Nepavyko patikrinti naujausios versijos!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>Nebuvo galima tęsti naujinimo, nes nėra jokio objekto, kuris yra susijęs su šiuo metu įdiegta versija! Automatinis naujinimas į tą versija yra neįmanoma.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>Nebuvo galima tęsti su atnaujinimu, nes ta versija neapima jokios informacijos!</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>Gautas prašymas reikalaujantis naudotojo įvesties, bet procesas veikia "headless" mode!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>Atsisakoma apdoroti užklausa, nes SteamOwnerID nėra nustatytas!</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>Stabdoma...</value>
|
||||
</data>
|
||||
@@ -196,10 +197,10 @@
|
||||
<value>Nepavyko!</value>
|
||||
</data>
|
||||
<data name="GlobalConfigChanged" xml:space="preserve">
|
||||
<value>Pasaulinis konfiguracijos failas buvo pakeistas!</value>
|
||||
<value>Pagrindinis konfiguracijos failas buvo pakeistas!</value>
|
||||
</data>
|
||||
<data name="ErrorGlobalConfigRemoved" xml:space="preserve">
|
||||
<value>Pasaulinis konfiguracijos failas buvo pašalintas!</value>
|
||||
<value>Pagrindinis konfiguracijos failas buvo pašalintas!</value>
|
||||
</data>
|
||||
<data name="IgnoringTrade" xml:space="preserve">
|
||||
<value>Ignoruojami mainai: {0}</value>
|
||||
@@ -239,7 +240,8 @@
|
||||
<value>Ieškoma naujos versijos...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Atsiunčiama nauja versija... Kol laukiate, apsvarstykite, gal norite paaukoti jei vertinate daromą darbą! :)</value>
|
||||
<value>Atsiunčiama nauja versija: {0} ({1} MB)... Kol laukiate, apsvarstykite, gal norite paaukoti jei vertinate daromą darbą! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Naujinimo procesas baigtas!</value>
|
||||
@@ -279,7 +281,10 @@
|
||||
<value>Prašome įvesti nedokumentuotą {0} reikšmę: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>Prašome įvesti savo IPC hostingą: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>Gauta nežinoma reikšmė, {0}, prašome pranešti apie tai: {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -288,10 +293,17 @@
|
||||
<value>Žaisti daugiau negu {0} žaidimų vienu metu yra neįmanoma, bus naudojamas tik pirmasis {0} įrašas iš {1}!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>Atsakyta į IPC komandą: {0} su: {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>IPC serveris paruoštas!</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>Paleidžiamas IPC serveris {0}...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Šis botas jau sustabdytas!</value>
|
||||
</data>
|
||||
@@ -543,10 +555,6 @@
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Botas šiuo metu yra naudojamas.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Nepavyko prisijungti prie Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Nepavyko prisijungti į Steam: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -655,4 +663,8 @@
|
||||
<value>Baigta peržiūrėti Steam atradimo eilė #{0}.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>Šiuo metu {0}/{1} botai turi visus tikrinamus žaidimus.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -172,10 +172,6 @@ StackTrace:
|
||||
<value>Verwerking van {0} mislukt!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Het oude ASF-bestand kon niet worden verwijderd. Verwijder {0} handmatig, zodat de update uitgevoerd kan worden!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>Verzoek is mislukt na {0} pogingen!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -183,14 +179,19 @@ StackTrace:
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>Controle voor de laatste versie is mislukt!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>Kon niet verdergaan met updaten, omdat er geen bestand gerelateerd is aan de reeds werkende versie! Automatisch updaten van deze versie is niet mogelijk.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>Kon niet verdergaan met updaten, omdat deze updateversie geen bestanden bevat!</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>Aanvraag voor gebruikersinvoer ontvangen, maar het proces draait in de headless mode!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>Het verzoek wordt niet in behandeling genomen omdat het SteamOwnerID niet is ingesteld!</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>Afsluiten...</value>
|
||||
</data>
|
||||
@@ -241,7 +242,8 @@ StackTrace:
|
||||
<value>Controleren op nieuwe versie...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Nieuwe versie wordt gedownload... Als je het gedane werk waardeert, overweeg dan tijdens het wachten om te doneren! :)</value>
|
||||
<value>Nieuwe versie wordt gedownload: {0} ({1} MB)... Als je het gedane werk waardeert, overweeg dan tijdens het wachten om te doneren! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Update is afgerond!</value>
|
||||
@@ -282,7 +284,10 @@ StackTrace:
|
||||
<value>Geef de ongedocumenteerde waarde van {0} op: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>Voer je IPC host in: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>Onbekende waarde ontvangen voor {0}, gelieven dit melden: {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -291,10 +296,17 @@ StackTrace:
|
||||
<value>Het gelijktijdig spelen van meer dan {0} spellen is niet mogelijk, alleen de eerste {0} spellen van {1} worden gespeeld!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>Gereageerd op IPC opdracht: {0} met: {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>IPC server gereed!</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>IPC server starten op {0}...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Deze bot is al gestopt!</value>
|
||||
</data>
|
||||
@@ -546,10 +558,6 @@ StackTrace:
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Bot is momenteel in gebruik.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Kan geen verbinding maken met Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Kan niet inloggen op Steam: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -658,4 +666,8 @@ StackTrace:
|
||||
<value>Steam-ontdekkingswachtrij voltooid #{0}.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>Er zijn {0}/{1} bots die de gecontroleerde spellen al in bezit hebben.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -172,10 +172,6 @@ StackTrace:
|
||||
<value>Verwerking van {0} mislukt!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Het oude ASF-bestand kon niet worden verwijderd. Verwijder {0} handmatig, zodat de update uitgevoerd kan worden!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>Verzoek is mislukt na {0} pogingen!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -183,14 +179,19 @@ StackTrace:
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>Laatste versie kon niet worden gecontroleerd!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>Kon niet verdergaan met updaten, omdat er geen bestand gerelateerd is aan de reeds werkende versie! Automatisch updaten van deze versie is niet mogelijk.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>Kon niet verdergaan met updaten, omdat deze updateversie geen bestanden bevat!</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>Aanvraag voor gebruikersinvoer ontvangen, maar het proces draait in de headless mode!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>Het verzoek wordt niet in behandeling genomen omdat het SteamOwnerID niet is ingesteld!</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>Afsluiten...</value>
|
||||
</data>
|
||||
@@ -241,7 +242,8 @@ StackTrace:
|
||||
<value>Controleren op nieuwe versie...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Nieuwe versie wordt gedownload... Als je het gedane werk waardeert, overweeg dan tijdens het wachten om te doneren! :)</value>
|
||||
<value>Nieuwe versie wordt gedownload: {0} ({1} MB)... Als je het gedane werk waardeert, overweeg dan tijdens het wachten om te doneren! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Update is afgerond!</value>
|
||||
@@ -282,7 +284,10 @@ StackTrace:
|
||||
<value>Geef de ongedocumenteerde waarde van {0} op: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>Voer je IPC host in: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>Onbekende waarde ontvangen voor {0}, graag dit melden: {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -291,10 +296,17 @@ StackTrace:
|
||||
<value>Het gelijktijdig spelen van meer dan {0} spellen is niet mogelijk, alleen de eerste {0} spellen van {1} worden gespeeld!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>Gereageerd op IPC opdracht: {0} met: {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>IPC server gereed!</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>IPC server starten op {0}...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Deze bot is al gestopt!</value>
|
||||
</data>
|
||||
@@ -546,10 +558,6 @@ StackTrace:
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Bot is momenteel in gebruik.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Kan geen verbinding maken met Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Kan niet inloggen op Steam: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -658,4 +666,8 @@ StackTrace:
|
||||
<value>Steam-ontdekkingswachtrij voltooid #{0}.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>Er zijn {0}/{1} bots die de gecontroleerde spellen al in bezit hebben.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -143,7 +143,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>Avslutter...</value>
|
||||
</data>
|
||||
@@ -196,7 +195,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="Done" xml:space="preserve">
|
||||
@@ -286,10 +284,6 @@
|
||||
|
||||
|
||||
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Kan ikke koble til Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Kan ikke logge inn på Steam: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -340,4 +334,5 @@
|
||||
|
||||
|
||||
|
||||
|
||||
</root>
|
||||
|
||||
@@ -173,10 +173,6 @@ StackTrace:
|
||||
<value>Analizowanie {0} nie powiodło się!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Nie można usunąć starej binarki ASF. Usuń ręcznie {0} aby naprawić funkcję aktualizacji!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>Żądanie nie powiodło się, po {0} próbach!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -247,7 +243,8 @@ StackTrace:
|
||||
<value>Wyszukiwanie nowej wersji...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Pobieranie nowej wersji... Podczas czekania rozważ dotację, jeśli doceniasz naszą pracę! :)</value>
|
||||
<value>Pobieranie nowej wersji: {0} ({1} MB)... Podczas czekania rozważ dotację, jeśli doceniasz naszą pracę! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Aktualizacja została zakończona!</value>
|
||||
@@ -299,9 +296,6 @@ StackTrace:
|
||||
<value>Uruchomienie więcej niż {0} gier naraz nie jest możliwe, jedynie pierwsze {0} gier z {1} zostanie użytych!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
<data name="ErrorIPCAddressAccessDeniedException" xml:space="preserve">
|
||||
<value>Usługa IPC nie mogła zostać uruchomiona z powodu błędu AddressAccessDeniedException! Jeżeli chcesz używać usługi IPC dostarczonej przez ASF, przemyśl uruchomienie ASF jako administrator lub przyznanie odpowiednich uprawnień!</value>
|
||||
</data>
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>Odpowiedziano na komendę IPC: {0} wiadomością: {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
@@ -564,10 +558,6 @@ StackTrace:
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Bot jest aktualnie używany.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Nie można połączyć ze Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Nie można zalogować do Steam: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -676,4 +666,8 @@ StackTrace:
|
||||
<value>Ukończono czyszczenie #{0} kolejki odkryć Steam.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>W tej chwili {0}/{1} botów posiada już wszystkie sprawdzane gry.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -173,10 +173,6 @@ StackTrace:
|
||||
<value>Falha ao analisar {0}!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Não foi possível remover o arquivo {0}. Por favor, remova-o manualmente para que a atualização funcione normalmente!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>A solicitação falhou após {0} tentativas!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -184,14 +180,19 @@ StackTrace:
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>Não foi possível verificar a última versão!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>Não foi possível prosseguir com a atualização porque não há nenhum recurso relacionado a versão atualmente em execução! Atualização automática para esta versão não é possível.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>Não foi possível prosseguir com a atualização pois esta versão não inclui nenhum arquivo!</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>Recebido um pedido de entrada feito pelo usuário, mas o processo está sendo executado em modo headless!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>Recusando-se a lidar com a requisição porque SteamOwnerID não está configurado!</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>Saindo...</value>
|
||||
</data>
|
||||
@@ -242,7 +243,8 @@ StackTrace:
|
||||
<value>Verificando se há atualizações...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Baixando nova versão... Enquanto aguarda, considere fazer uma doação caso aprecie o trabalho que está sendo feito! :)</value>
|
||||
<value>Baixando nova versão: {0} ({1} MB)... Enquanto espera, considere doar caso aprecie o nosso trabalho! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Processo de atualização finalizado!</value>
|
||||
@@ -282,7 +284,10 @@ StackTrace:
|
||||
<value>Por favor, insira o valor não documentado de {0}: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>Por favor, insira o seu host IPC: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>Valor desconhecido recebido para {0}. Por favor, reporte isto: {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -291,10 +296,17 @@ StackTrace:
|
||||
<value>Não é possível jogar mais de {0} jogos ao mesmo tempo, apenas os primeiros {0} jogos de {1} serão usados!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>Respondeu ao comando IPC: {0} com: {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>Servidor IPC pronto!</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>Iniciando servidor IPC em {0}...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Este bot já parou!</value>
|
||||
</data>
|
||||
@@ -397,7 +409,7 @@ StackTrace:
|
||||
<comment>{0} will be replaced by giftID (number)</comment>
|
||||
</data>
|
||||
<data name="BotAccountLimited" xml:space="preserve">
|
||||
<value>Esta conta está limitada, processo de farm indisponível até que a restrição seja removida!</value>
|
||||
<value>Esta conta é limitada, processo de coleta indisponível até que a restrição seja removida!</value>
|
||||
</data>
|
||||
<data name="BotAddLicense" xml:space="preserve">
|
||||
<value>ID: {0} | Estado: {1}</value>
|
||||
@@ -427,13 +439,13 @@ StackTrace:
|
||||
<value>O processo de coleta automático foi pausado!</value>
|
||||
</data>
|
||||
<data name="BotAutomaticIdlingNowResumed" xml:space="preserve">
|
||||
<value>O processo de farm automático foi retomado!</value>
|
||||
<value>Coleta automática retomada!</value>
|
||||
</data>
|
||||
<data name="BotAutomaticIdlingPausedAlready" xml:space="preserve">
|
||||
<value>A coleta automática de cartas já está pausada!</value>
|
||||
</data>
|
||||
<data name="BotAutomaticIdlingPausedWithCountdown" xml:space="preserve">
|
||||
<value>Idle automático foi pausado! Você tem {0} para começar um jogo.</value>
|
||||
<value>Coleta automática pausada! Você tem {0} para iniciar um jogo.</value>
|
||||
<comment>{0} will be replaced by translated TimeSpan string (such as "5 minutes")</comment>
|
||||
</data>
|
||||
<data name="BotAutomaticIdlingResumedAlready" xml:space="preserve">
|
||||
@@ -511,7 +523,7 @@ StackTrace:
|
||||
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
|
||||
</data>
|
||||
<data name="BotRateLimitExceeded" xml:space="preserve">
|
||||
<value>Limite de taxas excedido; vamos tentar novamente depois do intervalo de {0}...</value>
|
||||
<value>Limite de tráfego excedido; tentaremos novamente após um intervalo de {0}...</value>
|
||||
<comment>{0} will be replaced by translated TimeSpan string (such as "25 minutes")</comment>
|
||||
</data>
|
||||
<data name="BotReconnecting" xml:space="preserve">
|
||||
@@ -544,11 +556,7 @@ StackTrace:
|
||||
<value>Bot está pausado ou funcionando em modo manual.</value>
|
||||
</data>
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Bot está sendo usado no momento.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Não foi possível conectar-se ao Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
<value>Bot está sendo usado.</value>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Não foi possível iniciar a sessão no Steam: {0}/{1}</value>
|
||||
@@ -567,16 +575,16 @@ StackTrace:
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotConnectionLost" xml:space="preserve">
|
||||
<value>Conexão com a rede Steam perdida. Reconectando...</value>
|
||||
<value>A conexão à rede Steam foi perdida. Reconectando...</value>
|
||||
</data>
|
||||
<data name="BotAccountFree" xml:space="preserve">
|
||||
<value>A conta não está mais sendo usada: processo de coleta de cartas retomado!</value>
|
||||
</data>
|
||||
<data name="BotAccountOccupied" xml:space="preserve">
|
||||
<value>A conta está sendo usada no momento, o ASF voltará a farmar quando ela estiver livre...</value>
|
||||
<value>Conta em uso, o ASF retomará a coleta quando ela estiver disponível...</value>
|
||||
</data>
|
||||
<data name="BotAutomaticIdlingPauseTimeout" xml:space="preserve">
|
||||
<value>A biblioteca compartilhada não foi iniciada no período de tempo especificado, o farm foi retomado!</value>
|
||||
<value>A biblioteca compartilhada não foi iniciada no período de tempo especificado. O processo de coleta foi retomado!</value>
|
||||
</data>
|
||||
<data name="BotConnecting" xml:space="preserve">
|
||||
<value>Conectando...</value>
|
||||
@@ -651,11 +659,15 @@ StackTrace:
|
||||
<comment>{0} will be replaced by number (in megabytes) of memory being used</comment>
|
||||
</data>
|
||||
<data name="ClearingDiscoveryQueue" xml:space="preserve">
|
||||
<value>Limpando a fila de descoberta do Steam #{0}...</value>
|
||||
<value>Limpando a lista de descobrimento #{0}...</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="DoneClearingDiscoveryQueue" xml:space="preserve">
|
||||
<value>Limpeza da lista de descoberta da Steam #{0} concluída.</value>
|
||||
<value>Limpeza da lista de descobrimento #{0} concluída.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>Há {0} de {1} bots que já possuem todos os jogos sendo verificados.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -121,7 +121,10 @@
|
||||
<value>Aceitando a troca: {0}</value>
|
||||
<comment>{0} will be replaced by trade number</comment>
|
||||
</data>
|
||||
|
||||
<data name="AutoUpdateCheckInfo" xml:space="preserve">
|
||||
<value>ASF vai automaticamente procurar por novas versões a cada {0}.</value>
|
||||
<comment>{0} will be replaced by translated TimeSpan string (such as "24 hours")</comment>
|
||||
</data>
|
||||
<data name="Content" xml:space="preserve">
|
||||
<value>Conteúdo:
|
||||
{0}</value>
|
||||
@@ -132,7 +135,7 @@
|
||||
<comment>{0} will be replaced by name of the configuration property, {1} will be replaced by invalid value</comment>
|
||||
</data>
|
||||
<data name="ErrorEarlyFatalExceptionInfo" xml:space="preserve">
|
||||
<value>ASF V{0} foi executado em exceção fatal antes do núcleo do módulo de log foi capaz de inicializar!</value>
|
||||
<value>ASF V{0} encontrou uma exceção fatal antes que o núcleo do módulo de log foi sequer capaz de inicializar!</value>
|
||||
<comment>{0} will be replaced by version number</comment>
|
||||
</data>
|
||||
<data name="ErrorEarlyFatalExceptionPrint" xml:space="preserve">
|
||||
@@ -170,10 +173,6 @@ StackTrace:
|
||||
<value>Falha ao analisar {0}!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Não foi possível remover o velho ASF, por favor remova {0} manualmente para a função de atualização funcionar!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>O pedido falhou após {0} tentativas!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -181,16 +180,21 @@ StackTrace:
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>Não foi possível verificar a ultima versão!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>Não se conseguiu prosseguir com a atualização porque não há nenhum recurso que se relacione com a versão atual! Foi impossível atualizar automaticamente para essa versão.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>Não foi possível prosseguir com uma atualização!</value>
|
||||
<value>Não foi possível prosseguir com a atualização porque essa versão não incluí recursos!</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>O pedido de entrada do usuário foi recebido, mas o processo está sendo executado no modo não-interativo!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>Recusando-se a lidar com o pedido, porque SteamOwnerID não está definido!</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>A sair...</value>
|
||||
<value>Saindo...</value>
|
||||
</data>
|
||||
<data name="WarningFailed" xml:space="preserve">
|
||||
<value>Erro!</value>
|
||||
@@ -206,27 +210,27 @@ StackTrace:
|
||||
<comment>{0} will be replaced by trade number</comment>
|
||||
</data>
|
||||
<data name="LoggingIn" xml:space="preserve">
|
||||
<value>A entrar em {0}...</value>
|
||||
<value>A iniciar sessão em {0}...</value>
|
||||
<comment>{0} will be replaced by service's name</comment>
|
||||
</data>
|
||||
<data name="NoBotsAreRunning" xml:space="preserve">
|
||||
<value>Sem bots ligados, a sair...</value>
|
||||
</data>
|
||||
<data name="RefreshingOurSession" xml:space="preserve">
|
||||
<value>Atualizando a nossa sessão!</value>
|
||||
<value>A atualizar a nossa sessão!</value>
|
||||
</data>
|
||||
<data name="RejectingTrade" xml:space="preserve">
|
||||
<value>Rejeitando a troca: {0}</value>
|
||||
<comment>{0} will be replaced by trade number</comment>
|
||||
</data>
|
||||
<data name="Restarting" xml:space="preserve">
|
||||
<value>Reniciando...</value>
|
||||
<value>Reiniciando...</value>
|
||||
</data>
|
||||
<data name="Starting" xml:space="preserve">
|
||||
<value>A iniciar...</value>
|
||||
<value>Iniciando...</value>
|
||||
</data>
|
||||
<data name="StatusCode" xml:space="preserve">
|
||||
<value>Estado do código: {0}</value>
|
||||
<value>Código de estado: {0}</value>
|
||||
<comment>{0} will be replaced by status code number/name</comment>
|
||||
</data>
|
||||
<data name="Success" xml:space="preserve">
|
||||
@@ -239,13 +243,14 @@ StackTrace:
|
||||
<value>A verificar por novas versões...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>A obter a nova versão... Enquanto espera, considere doar algo se aprecias o trabalho feito! :)</value>
|
||||
<value>Obtendo a nova versão: {0} ({1} MB)... Enquanto esperas, considera doar algo se aprecias o trabalho feito! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Processo de actualização terminada!</value>
|
||||
</data>
|
||||
<data name="UpdateNewVersionAvailable" xml:space="preserve">
|
||||
<value>Nova versão do ASF está disponível! Considere obter-la!</value>
|
||||
<value>Há uma nova versão do ASF disponível! Considere atualizar!</value>
|
||||
</data>
|
||||
<data name="UpdateVersionInfo" xml:space="preserve">
|
||||
<value>Versão local: {0} | Versão remota: {1}</value>
|
||||
@@ -279,7 +284,10 @@ StackTrace:
|
||||
<value>Por favor, insira o valor não-documentado de {0}: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>Por favor insira o seu anfitrião de IPC: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>Valor desconhecido recebido por {0}, por favor reporte isto: {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -288,10 +296,17 @@ StackTrace:
|
||||
<value>Não é possível jogar {0} ao mesmo tempo, apenas {0} jogos vão usados com {1}!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>Respondeu ao comando do IPC: {0} com: {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>Servidor IPC pronto!</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>Iniciando o servidor IPC em {0}...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Este bot já parou!</value>
|
||||
</data>
|
||||
@@ -429,7 +444,10 @@ StackTrace:
|
||||
<data name="BotAutomaticIdlingPausedAlready" xml:space="preserve">
|
||||
<value>A coleta automática está pausada!</value>
|
||||
</data>
|
||||
|
||||
<data name="BotAutomaticIdlingPausedWithCountdown" xml:space="preserve">
|
||||
<value>A coleta automática está agora em pausa. Tens {0} para iniciar um jogo.</value>
|
||||
<comment>{0} will be replaced by translated TimeSpan string (such as "5 minutes")</comment>
|
||||
</data>
|
||||
<data name="BotAutomaticIdlingResumedAlready" xml:space="preserve">
|
||||
<value>A coleta automática já foi resumida!</value>
|
||||
</data>
|
||||
@@ -489,7 +507,7 @@ inválidas, abortando!</value>
|
||||
<value>Looting está temporariamente desligado!</value>
|
||||
</data>
|
||||
<data name="BotLootingYourself" xml:space="preserve">
|
||||
<value>Não se pode bloquear a si mesmo!</value>
|
||||
<value>Não podes colecionar os seus próprios itens!</value>
|
||||
</data>
|
||||
<data name="BotNoASFAuthenticator" xml:space="preserve">
|
||||
<value>Este bot não tem a ASF 2FA ligada! Esqueceu-se de importar seu autenticador como ASF 2FA?</value>
|
||||
@@ -505,7 +523,10 @@ inválidas, abortando!</value>
|
||||
<value>Já adquirido: {0} | {1}</value>
|
||||
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
|
||||
</data>
|
||||
|
||||
<data name="BotRateLimitExceeded" xml:space="preserve">
|
||||
<value>Taxa limite excedida, tentaremos novamente daqui a {0}...</value>
|
||||
<comment>{0} will be replaced by translated TimeSpan string (such as "25 minutes")</comment>
|
||||
</data>
|
||||
<data name="BotReconnecting" xml:space="preserve">
|
||||
<value>Reconectando...</value>
|
||||
</data>
|
||||
@@ -538,10 +559,6 @@ inválidas, abortando!</value>
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>O Bot está a ser usado atualmente.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Não foi possível estabelecer ligação à Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Não foi possível efectuar o login para a Steam: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -635,8 +652,23 @@ inválidas, abortando!</value>
|
||||
<data name="ErrorAccessDenied" xml:space="preserve">
|
||||
<value>Acesso negado!</value>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="WarningPreReleaseVersion" xml:space="preserve">
|
||||
<value>Estás a usar uma versão mais recente que a última versão do teu canal de atualização. Por favor considera que as versões de pré-lançamento são dedicados a utilizadores que sabem reportar erros, lidar com problemas e dar feedback - não irás receber suporte técnico.</value>
|
||||
</data>
|
||||
<data name="BotStats" xml:space="preserve">
|
||||
<value>Uso de memória atual: {0} MB.</value>
|
||||
<comment>{0} will be replaced by number (in megabytes) of memory being used</comment>
|
||||
</data>
|
||||
<data name="ClearingDiscoveryQueue" xml:space="preserve">
|
||||
<value>Passando a Fila de Exploração da Steam #{0}...</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="DoneClearingDiscoveryQueue" xml:space="preserve">
|
||||
<value>Fila de exploração da Steam feita #{0}.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>Os bots {0}/{1} já possuem todos os jogos que está sendo verificados.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -173,10 +173,6 @@ StackTrace:
|
||||
<value>Parsing {0} failed!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Could not remove old ASF binary. Please remove {0} manually in order for update function to work!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>Request failed after {0} attempts!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -247,7 +243,8 @@ StackTrace:
|
||||
<value>Checking for new version...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Downloading new version... While waiting, consider donating if you appreciate the work being done! :)</value>
|
||||
<value>Downloading new version: {0} ({1} MB)... While waiting, consider donating if you appreciate the work being done! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Update process finished!</value>
|
||||
@@ -299,9 +296,6 @@ StackTrace:
|
||||
<value>Playing more than {0} games concurrently is not possible, only first {0} entries from {1} will be used!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
<data name="ErrorIPCAddressAccessDeniedException" xml:space="preserve">
|
||||
<value>IPC service could not be started because of AddressAccessDeniedException! If you want to use IPC service provided by ASF, consider starting ASF as administrator, or giving proper permissions!</value>
|
||||
</data>
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>Answered to IPC command: {0} with: {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
@@ -564,10 +558,6 @@ StackTrace:
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Bot is currently being used.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Unable to connect to Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Unable to login to Steam: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -676,4 +666,8 @@ StackTrace:
|
||||
<value>Done clearing Steam discovery queue #{0}.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>There are {0}/{1} bots that already own all of the games being checked.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -173,10 +173,6 @@ StackTrace:
|
||||
<value>Parsarea {0} a eșuat!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Nu a fost posibilă eliminarea ultimului executabil ASF. Te rog să elimini manual {0} pentru ca funcția de actualizare să funcționeze!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>Cererea a eșuat după {0} încercări!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -184,14 +180,19 @@ StackTrace:
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>Nu a fost posibilă verificarea celei mai recente versiuni!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>Nu s-a putut continua cu actualizarea deoarece nu există niciun fișier asemănător versiunii care rulează! Actualizarea automată către acea versiune nu este posibilă.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>Nu putem continua cu actualizarea deoarece acea versiune nu conține niciun fișier!</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>S-a primit o cerere de date introduse de utilizator, dar procesul se execută în modul fără cap (serviciu)!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>Se refuză gestionarea cererii, deoarece SteamOwnerID nu este setat!</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>Ieșire...</value>
|
||||
</data>
|
||||
@@ -242,7 +243,8 @@ StackTrace:
|
||||
<value>Se caută versiune nouă...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Se descarcă o versiune nouă... Cât timp aștepți, dacă consideri, poți dona dacă apreciezi toată munca depusă! :)</value>
|
||||
<value>Se descarcă versiunea nouă: {0} ({1} MB)... Cât timp aștepți, ia în cosiderare donarea dacă apreciezi munca depusă! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Proces de actualizare finalizat!</value>
|
||||
@@ -282,7 +284,10 @@ StackTrace:
|
||||
<value>Te rog să introduci valoarea nedocumentată a {0}: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>Te rugăm să introduci gazda IPC-ului tău: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>Am primit o valoare necunoscută pentru {0}, te rog să raportezi acest lucru: {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -291,10 +296,17 @@ StackTrace:
|
||||
<value>Jucarea a mai mult de {0} jocuri concomitent nu este posibil, numai primele {0} intrări de la {1} vor fi folosite!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>Se răspunde la comanda IPC: {0} cu: {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>Serverul IPC este pregătit!</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>Pornește serverul IPC pe {0}...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Acest bot s-a oprit deja!</value>
|
||||
</data>
|
||||
@@ -546,10 +558,6 @@ StackTrace:
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Botul este folosit în prezent.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Nu s-a putut realiza conexiunea la Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Nu s-a putut autentifica la Steam: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -658,4 +666,8 @@ StackTrace:
|
||||
<value>S-a terminat ștergerea cozii pentru lista de descoperiri Steam #{0}.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>Există {0}/{1} boți care dețin deja toate jocurile verificate.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -173,10 +173,6 @@
|
||||
<value>Обработка {0} не удалась!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Невозможно удалить старый исполняемый файл ASF. Пожалуйста, удалите {0} самостоятельно, для восстановления функции автоматического обновления!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>Запрос окончился неудачей после {0} попыток!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -184,14 +180,19 @@
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>Не удалось проверить последнюю версию!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>Невозможно произвести обновление, так как нет подходящих файлов, которые относятся к текущей запущенной версии! Автоматическое обновление до этой версии невозможно.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>Не могу обновиться, поскольку эта версия не содержит никаких файлов!</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>Получен запрос на ввод данных пользователем, однако процесс идёт в безынтерфейсном режиме!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>Отказ от обработки запроса, поскольку SteamOwnerID не задан!</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>Выход...</value>
|
||||
</data>
|
||||
@@ -242,7 +243,8 @@
|
||||
<value>Проверка новой версии...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Загрузка новой версии... Во время ожидания подумайте о пожертвовании в пользу разработчиков, если вы цените проделанную работу! :)</value>
|
||||
<value>Скачивание новой версии: {0} ({1} MB)... А пока, подумайте о пожертвовании, если вам нравится наша работа! ;)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Процесс обновления завершён!</value>
|
||||
@@ -282,7 +284,10 @@
|
||||
<value>Пожалуйста, введите недокументированное значение {0}: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>Пожалуйста, введите ваш хост IPC: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>Получено неизвестное значение для {0}, пожалуйста, сообщите об этом: {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -291,10 +296,17 @@
|
||||
<value>Невозможен запуск более {0} игр одновременно, будут использованы только первые {0} записей из параметра {1}!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>На запрос IPC: {0} дан ответ: {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>IPC сервер готов!</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>Запуск IPC сервера на {0}...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Этот бот уже остановлен!</value>
|
||||
</data>
|
||||
@@ -546,10 +558,6 @@
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Бот сейчас используется.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Не удалось подключиться к Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Не удалось войти в Steam: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -658,4 +666,8 @@
|
||||
<value>Очищен список рекомендаций #{0}.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>Имеются {0} / {1} ботов, которые уже владеют всеми проверенными играми.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -173,10 +173,6 @@ StackTrace:
|
||||
<value>Spracovávanie {0} zlyhalo!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Nepodarilo sa odstrániť starý binárny súbor ASF. Aby aktualizačná funkcia pracovala správne, odstráň prosím súbor {0} ručne!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>Požiadavka zlyhala po {0} pokusoch!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -241,9 +237,7 @@ StackTrace:
|
||||
<data name="UpdateCheckingNewVersion" xml:space="preserve">
|
||||
<value>Kontrola najnovšej verzie...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Sťahovanie najnovšej verzie... Zváž podporu tohto projektu počas čakania! :)</value>
|
||||
</data>
|
||||
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Aktualizácia úspešne dokončená!</value>
|
||||
</data>
|
||||
@@ -294,7 +288,6 @@ StackTrace:
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Tento bot už bol zastavený!</value>
|
||||
</data>
|
||||
@@ -546,10 +539,6 @@ StackTrace:
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Bot je práve používaný.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Nie je možné pripojiť sa k službe Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Nie je možné prihlásenie do služby Steam: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -652,4 +641,5 @@ StackTrace:
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
</root>
|
||||
|
||||
@@ -164,7 +164,6 @@ StackTrace:
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
|
||||
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>Neuspešno traženje nove verzije!</value>
|
||||
</data>
|
||||
@@ -225,9 +224,7 @@ StackTrace:
|
||||
<data name="UpdateCheckingNewVersion" xml:space="preserve">
|
||||
<value>Traženje nove verzije...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Skidanje nove verzije... Dok čekate, razmislite o podržavanju ovoh programa putem donacija ako cenite uložen rad! :)</value>
|
||||
</data>
|
||||
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Proces ažuriranja je završen!</value>
|
||||
</data>
|
||||
@@ -258,7 +255,6 @@ StackTrace:
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="BotNotFound" xml:space="preserve">
|
||||
<value>Nije moguće pronaći bilo kakvog bot-a nazvanog {0}!</value>
|
||||
<comment>{0} will be replaced by bot's name query (string)</comment>
|
||||
@@ -448,10 +444,6 @@ StackTrace:
|
||||
|
||||
|
||||
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Nije moguće povezati se na Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Nije moguće prijavljivanje na Steam: {0}{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -512,4 +504,5 @@ StackTrace:
|
||||
|
||||
|
||||
|
||||
|
||||
</root>
|
||||
|
||||
@@ -152,7 +152,7 @@ StackTrace:
|
||||
<comment>{0} will be replaced by URL of the request</comment>
|
||||
</data>
|
||||
<data name="ErrorGlobalConfigNotLoaded" xml:space="preserve">
|
||||
<value>Den globala konfigurationen kunde inte laddas. Försäkra dig om att {0} existerar och är valid! Följ installations-guiden på wiki-sidan om du är förvirrad.</value>
|
||||
<value>Den globala konfigurationen kunde inte laddas. Försäkra dig om att {0} existerar och är giltlig! Följ installationsguiden på wikisidan om du är förvirrad.</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorIsInvalid" xml:space="preserve">
|
||||
@@ -173,10 +173,6 @@ StackTrace:
|
||||
<value>Tolkning {0} misslyckades!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Kunde inte ta bort den gamla ASF binären, vänligen ta bort {0} manuellt för att uppdateringsfunktionen ska fungera!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>Begäran misslyckades efter {0} försök!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -184,14 +180,19 @@ StackTrace:
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>Kunde inte kontrollera senaste versionen!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>Kunde inte fortsätta med uppdateringen eftersom det inte finns någon tillgång som relaterar till den nuvarande versionen! Automatisk uppdatering till denna version är inte möjlig.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>Kunde inte fortsätta med en uppdatering för den versionen innehåller inte några filer!</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>Mottagit en begäran om användarens input, men processen körs i huvudlöst läge!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>Vägrar att hantera begäran eftersom SteamOwnerID inte är inställt!</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>Stänger ner...</value>
|
||||
</data>
|
||||
@@ -205,7 +206,7 @@ StackTrace:
|
||||
<value>Globala konfigurationsfilen har tagits bort!</value>
|
||||
</data>
|
||||
<data name="IgnoringTrade" xml:space="preserve">
|
||||
<value>Avböjer byte: {0}</value>
|
||||
<value>Ignorerar bytesförfrågan: {0}</value>
|
||||
<comment>{0} will be replaced by trade number</comment>
|
||||
</data>
|
||||
<data name="LoggingIn" xml:space="preserve">
|
||||
@@ -216,7 +217,7 @@ StackTrace:
|
||||
<value>Inga bottar körs just nu, stänger ner...</value>
|
||||
</data>
|
||||
<data name="RefreshingOurSession" xml:space="preserve">
|
||||
<value>Startar om våran session!</value>
|
||||
<value>Startar om sessionen!</value>
|
||||
</data>
|
||||
<data name="RejectingTrade" xml:space="preserve">
|
||||
<value>Avböjer bytesförfrågan: {0}</value>
|
||||
@@ -236,14 +237,15 @@ StackTrace:
|
||||
<value>Framgång!</value>
|
||||
</data>
|
||||
<data name="UnlockingParentalAccount" xml:space="preserve">
|
||||
<value>Låser upp föräldrarnas konto...</value>
|
||||
<value>Låser upp föräldrarkontot...</value>
|
||||
</data>
|
||||
<data name="UpdateCheckingNewVersion" xml:space="preserve">
|
||||
<value>Söker efter senaste versionen...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Laddar ner senaste versionen... Medan du väntar, fundera på att donera om du uppskattar arbetet som görs!
|
||||
<value>Laddar ner senaste versionen: {0} ({1} MB)... Medan du väntar, fundera på att donera om du uppskattar arbetet som görs!
|
||||
:)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Uppdateringsprocessen klar!</value>
|
||||
@@ -256,7 +258,7 @@ StackTrace:
|
||||
<comment>{0} will be replaced by current version, {1} will be replaced by remote version</comment>
|
||||
</data>
|
||||
<data name="UserInputDeviceID" xml:space="preserve">
|
||||
<value>Vänligen ange din mobil-autentiserares enhets-ID (inklusive "android:"): </value>
|
||||
<value>Vänligen ange mobil-autentiserarens enhets-ID (inklusive "android:"): </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="UserInputSteam2FA" xml:space="preserve">
|
||||
@@ -283,7 +285,10 @@ StackTrace:
|
||||
<value>Vänligen ange odokumenterade värde av {0}: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>Vänligen ange din IPC Host: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>Tog emot okänt värde för {0}, vänligen rapportera detta: {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -292,10 +297,17 @@ StackTrace:
|
||||
<value>Går inte att spela mer än {0} spel samtidigt, bara första {0} poster från {1} kommer att användas!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>Svarade till IPC kommandot {0} med: {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>IPC servern är redo!</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>Startar IPC servern på {0}...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Bot-instansen har redan stoppats!</value>
|
||||
</data>
|
||||
@@ -547,10 +559,6 @@ StackTrace:
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Bot används för närvarande.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Kan inte ansluta till Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Det går inte att logga in på Steam: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -647,7 +655,20 @@ StackTrace:
|
||||
<data name="WarningPreReleaseVersion" xml:space="preserve">
|
||||
<value>Du använder en version som är nyare än den senast släppta versionen i din uppdateringskanal. Vänligen notera att förhandsversioner är avsedda för användare med förmågan att rapportera buggar, handskas med eventuella problem och viljan att ge feedback - Ingen teknisk support kommer att ges.</value>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
<data name="BotStats" xml:space="preserve">
|
||||
<value>Nuvarande minnesförbrukning: {0} MB.</value>
|
||||
<comment>{0} will be replaced by number (in megabytes) of memory being used</comment>
|
||||
</data>
|
||||
<data name="ClearingDiscoveryQueue" xml:space="preserve">
|
||||
<value>Genomgår Steam discovery-kön #{0}...</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="DoneClearingDiscoveryQueue" xml:space="preserve">
|
||||
<value>Genomgått Steam discovery-kön #{0}.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>Det finns {0}/{1} bottar som redan äger samtliga spel som kontrollerats.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -173,10 +173,6 @@ Yığın izleme:
|
||||
<value>{0} nesnesi işlenirken bir hata oluştu!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Eski ASF ikili dosyası kaldırılamadı, lütfen güncelleme fonksiyonunun çalışması için {0} dosyasını el ile kaldırın!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>{0} denemeden sonra istek başarısız oldu!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -184,14 +180,19 @@ Yığın izleme:
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>En son sürüm kontrol edilemedi!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>Şu anda çalışan sürüme ait hiçbir dosya olmadığı için güncelleme işlemine devam edemedi! Bu sürüme otomatik güncelleme yapmak mümkün değil.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>Sürüm herhangi bir öğe içermediğinden güncelleme ile devam edemedi!</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>Kullanıcı girdisi için bir istek alındı; ancak işlem headless modda çalışıyor!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>SteamOwnerID ayarlanmadığı için istek reddedildi!</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>Çıkılıyor...</value>
|
||||
</data>
|
||||
@@ -242,7 +243,8 @@ Yığın izleme:
|
||||
<value>Yeni sürüm kontrol ediliyor...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Yeni sürüm indiriliyor... Beklerken, yaptığımız çalışmayı takdir ediyorsanız bağış yapmayı düşünün! :)</value>
|
||||
<value>Yeni sürüm indiriliyor: {0} ({1} MB)... Beklerken, yaptığımız çalışmayı takdir ediyorsanız bağış yapmayı düşünün! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Güncelleme işlemi tamamlandı!</value>
|
||||
@@ -282,7 +284,10 @@ Yığın izleme:
|
||||
<value>Lütfen belgelenmemiş {0} değerini girin: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>Lütfen IPC sunucunuzu girin: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>{0} için bilinmeyen değer alındı, lütfen bunu bildirin: {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -291,10 +296,17 @@ Yığın izleme:
|
||||
<value>Eşzamanlı olarak {0} oyundan fazlasını oynamak mümkün değildir, yalnızca {1} yapılandırmasından ilk {0} girdisi kullanılacaktır!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>IPC komutu {0} için cevap: {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>IPC sunucusu hazır!</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>{0} sunucusunda IPC sunucusu başlatılıyor...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Bu bot zaten durdurulmuş!</value>
|
||||
</data>
|
||||
@@ -546,10 +558,6 @@ Yığın izleme:
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Bot şu anda kullanılıyor.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Steam'e bağlanılamadı: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Steam'e giriş yapılamıyor: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -658,4 +666,8 @@ Yığın izleme:
|
||||
<value>Steam keşif kuyruğu temizlenmesi bitti #{0}.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>{0}/{1} bot zaten sahip olunan tüm oyunları denetliyor.</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -173,10 +173,6 @@
|
||||
<value>Не вдалося обробити {0}!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Неможливо видалити старий виконуваний файл ASF. Будь ласка, видаліть {0} самостійно, для відновлення функції автоматичного оновлення!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>Не вдалося виконати запит після {0} спроб!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -241,9 +237,7 @@
|
||||
<data name="UpdateCheckingNewVersion" xml:space="preserve">
|
||||
<value>Перевірка наявності нової версії...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Завантаження нової версії... Під час очікування подумайте про пожертвування на користь розробників, якщо ви цінуєте виконану роботу! :)</value>
|
||||
</data>
|
||||
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Процесс оновлення закінчено!</value>
|
||||
</data>
|
||||
@@ -294,7 +288,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Цей бот вже зупинений!</value>
|
||||
</data>
|
||||
@@ -546,10 +539,6 @@
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Бот зараз використовується.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Неможливо з'єднатися зі Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Неможливо увійти до Steam: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -652,4 +641,5 @@
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
</root>
|
||||
|
||||
@@ -121,7 +121,10 @@
|
||||
<value>Chấp nhận giao dịch: {0}</value>
|
||||
<comment>{0} will be replaced by trade number</comment>
|
||||
</data>
|
||||
|
||||
<data name="AutoUpdateCheckInfo" xml:space="preserve">
|
||||
<value>ASF sẽ tự động kiểm tra phiên bản mới mỗi {0}.</value>
|
||||
<comment>{0} will be replaced by translated TimeSpan string (such as "24 hours")</comment>
|
||||
</data>
|
||||
<data name="Content" xml:space="preserve">
|
||||
<value>Nội dung:
|
||||
{0}</value>
|
||||
@@ -141,7 +144,9 @@ StackTrace:
|
||||
{2}</value>
|
||||
<comment>{0} will be replaced by function name, {1} will be replaced by exception message, {2} will be replaced by entire stack trace. Please note that this string should include newlines for formatting.</comment>
|
||||
</data>
|
||||
|
||||
<data name="ErrorExitingWithNonZeroErrorCode" xml:space="preserve">
|
||||
<value>Đang thoát với mã lỗi khác không!</value>
|
||||
</data>
|
||||
<data name="ErrorFailingRequest" xml:space="preserve">
|
||||
<value>Yêu cầu thất bại: {0}</value>
|
||||
<comment>{0} will be replaced by URL of the request</comment>
|
||||
@@ -168,10 +173,6 @@ StackTrace:
|
||||
<value>Phân tách ngữ pháp {0} không thành công!</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>Không thể xóa tập tin ASF cũ, vui lòng xóa thủ công {0} để chạy chức năng cập nhật!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>Yêu cầu thất bại sau {0} nỗ lực!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -179,14 +180,19 @@ StackTrace:
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>Không thể kiểm tra phiên bản mới nhất!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>Không thể tiến hành cập nhật vì không có asset liên quan đến phiên bản đang chạy! Tự động cập nhật lên phiên bản đó là không thể.</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>Không thể tiến hành với bản cập nhật vì phiên bản đó không gồm bất cứ tài sản nào!</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>Nhận được yêu cầu nhập của người dùng, nhưng quá trình đang chạy trong chế độ không kiểm soát!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>Từ chối xử lý yêu cầu do SteamOwnerID chưa được đặt!</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>Đang thoát...</value>
|
||||
</data>
|
||||
@@ -218,7 +224,7 @@ StackTrace:
|
||||
<comment>{0} will be replaced by trade number</comment>
|
||||
</data>
|
||||
<data name="Restarting" xml:space="preserve">
|
||||
<value>Khởi động lại...</value>
|
||||
<value>Đang khởi động lại...</value>
|
||||
</data>
|
||||
<data name="Starting" xml:space="preserve">
|
||||
<value>Bắt đầu...</value>
|
||||
@@ -237,7 +243,8 @@ StackTrace:
|
||||
<value>Kiểm tra phiên bản mới...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>Tải xuống phiên bản mới... Trong khi chờ đợi, hãy xem xét quyên góp nếu bạn đánh giá cao công việc được thực hiện! :)</value>
|
||||
<value>Đang tải phiên bản mới: {0} ({1} MB)... Trong khi chờ, hãy xem xét quyên góp nếu bạn đánh giá cao sản phẩm được thực hiện! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>Quá trình cập nhật hoàn tất!</value>
|
||||
@@ -277,7 +284,10 @@ StackTrace:
|
||||
<value>Xin vui lòng nhập các giá trị không có giấy tờ của {0}: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>Vui lòng nhập IPC host của bạn: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>Nhận được giá trị không rõ cho {0}, xin vui lòng báo cáo điều này: {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -286,10 +296,17 @@ StackTrace:
|
||||
<value>Chơi nhiều hơn {0} trò chơi đồng thời là không thể, chỉ {0} trò chơi đầu tiên {1} sẽ được sử dụng!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>Đã trả lời lệnh IPC: {0} với: {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>IPC server đã sẵn sàng!</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>Khởi động IPC server trên {0}...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>Bot này đã được dừng lại!</value>
|
||||
</data>
|
||||
@@ -301,8 +318,14 @@ StackTrace:
|
||||
<value>Đang có {0}/{1} bot đang chạy, với tổng số {2} games ({3} cards) còn lại để chạy không.</value>
|
||||
<comment>{0} will be replaced by number of active bots, {1} will be replaced by total number of bots, {2} will be replaced by total number of games left to idle, {3} will be replaced by total number of cards left to idle</comment>
|
||||
</data>
|
||||
|
||||
|
||||
<data name="BotStatusIdling" xml:space="preserve">
|
||||
<value>Bot đang cày game: {0} ({1}, {2} card còn lại) từ tổng cộng {3} game ({4} card) còn lại để cày (~{5} còn lại).</value>
|
||||
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name, {2} will be replaced by number of cards left to idle, {3} will be replaced by total number of games to idle, {4} will be replaced by total number of cards to idle, {5} will be replaced by translated TimeSpan string (such as "1 day, 5 hours and 30 minutes")</comment>
|
||||
</data>
|
||||
<data name="BotStatusIdlingList" xml:space="preserve">
|
||||
<value>Bot đang cày game: {0} từ tổng cộng {1} game ({2} card) còn lại để cày (~{3} còn lại).</value>
|
||||
<comment>{0} will be replaced by list of the games (IDs, numbers), {1} will be replaced by total number of games to idle, {2} will be replaced by total number of cards to idle, {3} will be replaced by translated TimeSpan string (such as "1 day, 5 hours and 30 minutes")</comment>
|
||||
</data>
|
||||
<data name="CheckingFirstBadgePage" xml:space="preserve">
|
||||
<value>Kiểm tra trang huy hiệu đầu tiên...</value>
|
||||
</data>
|
||||
@@ -388,8 +411,14 @@ StackTrace:
|
||||
<data name="BotAccountLimited" xml:space="preserve">
|
||||
<value>Tài khoản này bị giới hạn, quá trình chạy không sẽ không khả dụng cho đến khi những hạn chế sẽ bị gỡ bỏ!</value>
|
||||
</data>
|
||||
|
||||
|
||||
<data name="BotAddLicense" xml:space="preserve">
|
||||
<value>ID: {0} | Trạng thái: {1}</value>
|
||||
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by status string</comment>
|
||||
</data>
|
||||
<data name="BotAddLicenseWithItems" xml:space="preserve">
|
||||
<value>ID: {0} | Trạng thái: {1} | Mục: {2}</value>
|
||||
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by status string, {2} will be replaced by list of granted IDs (numbers), separated by a comma</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyRunning" xml:space="preserve">
|
||||
<value>Bot này đang được chạy!</value>
|
||||
</data>
|
||||
@@ -406,12 +435,19 @@ StackTrace:
|
||||
<value>2FA Token: {0}</value>
|
||||
<comment>{0} will be replaced by generated 2FA token (string)</comment>
|
||||
</data>
|
||||
|
||||
|
||||
<data name="BotAutomaticIdlingNowPaused" xml:space="preserve">
|
||||
<value>Tự động idling đã tạm dừng!</value>
|
||||
</data>
|
||||
<data name="BotAutomaticIdlingNowResumed" xml:space="preserve">
|
||||
<value>Tự động idling đã tiếp tục trở lại!</value>
|
||||
</data>
|
||||
<data name="BotAutomaticIdlingPausedAlready" xml:space="preserve">
|
||||
<value>Tự động chạy không đã tạm dừng!</value>
|
||||
</data>
|
||||
|
||||
<data name="BotAutomaticIdlingPausedWithCountdown" xml:space="preserve">
|
||||
<value>Tự động cày đã tạm dừng! Bạn có {0} để bắt đầu 1 game.</value>
|
||||
<comment>{0} will be replaced by translated TimeSpan string (such as "5 minutes")</comment>
|
||||
</data>
|
||||
<data name="BotAutomaticIdlingResumedAlready" xml:space="preserve">
|
||||
<value>Tự động chạy không đã tiếp tục!</value>
|
||||
</data>
|
||||
@@ -451,7 +487,9 @@ StackTrace:
|
||||
<data name="BotLootingFailed" xml:space="preserve">
|
||||
<value>Lời mời giao dịch thất bại!</value>
|
||||
</data>
|
||||
|
||||
<data name="BotLootingMasterNotDefined" xml:space="preserve">
|
||||
<value>Giao dịch không thể gửi do không có user có quyền master xác định!</value>
|
||||
</data>
|
||||
<data name="BotLootingNoLootableTypes" xml:space="preserve">
|
||||
<value>Bạn không có bất kỳ cái gì có thể luộc được!</value>
|
||||
</data>
|
||||
@@ -476,17 +514,29 @@ StackTrace:
|
||||
<data name="BotNotConnected" xml:space="preserve">
|
||||
<value>Bot này không được kết nối!</value>
|
||||
</data>
|
||||
|
||||
<data name="BotNotOwnedYet" xml:space="preserve">
|
||||
<value>Chưa sở hữu: {0}</value>
|
||||
<comment>{0} will be replaced by query (string)</comment>
|
||||
</data>
|
||||
<data name="BotOwnedAlreadyWithName" xml:space="preserve">
|
||||
<value>Đã sở hữu: {0} | {1}</value>
|
||||
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
|
||||
</data>
|
||||
|
||||
<data name="BotRateLimitExceeded" xml:space="preserve">
|
||||
<value>Vượt quá số lượng giới hạn; chúng tôi sẽ thử lại sau {0} đếm ngược...</value>
|
||||
<comment>{0} will be replaced by translated TimeSpan string (such as "25 minutes")</comment>
|
||||
</data>
|
||||
<data name="BotReconnecting" xml:space="preserve">
|
||||
<value>Đang kết nối lại...</value>
|
||||
</data>
|
||||
|
||||
|
||||
<data name="BotRedeem" xml:space="preserve">
|
||||
<value>Key: {0} | Status: {1}</value>
|
||||
<comment>{0} will be replaced by cd-key (string), {1} will be replaced by status string</comment>
|
||||
</data>
|
||||
<data name="BotRedeemWithItems" xml:space="preserve">
|
||||
<value>Key: {0} | Status: {1} | Items: {2}</value>
|
||||
<comment>{0} will be replaced by cd-key (string), {1} will be replaced by status string, {2} will be replaced by list of key-value pairs, separated by a comma</comment>
|
||||
</data>
|
||||
<data name="BotRemovedExpiredLoginKey" xml:space="preserve">
|
||||
<value>Gỡ bỏ key đăng nhập hết hạn!</value>
|
||||
</data>
|
||||
@@ -496,7 +546,9 @@ StackTrace:
|
||||
<data name="BotStatusLimited" xml:space="preserve">
|
||||
<value>Bot bị hạn chế và không thể rớt bất kỳ thẻ thông qua chạy không.</value>
|
||||
</data>
|
||||
|
||||
<data name="BotStatusConnecting" xml:space="preserve">
|
||||
<value>Bot đang kết nối với Steam network.</value>
|
||||
</data>
|
||||
<data name="BotStatusNotRunning" xml:space="preserve">
|
||||
<value>Bot không chạy.</value>
|
||||
</data>
|
||||
@@ -506,10 +558,6 @@ StackTrace:
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>Bot hiện đang được sử dụng.</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>Không thể kết nối với Steam: {0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>Không thể đăng nhập Steam: {0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -575,7 +623,10 @@ StackTrace:
|
||||
<value>ASF sẽ cố gắng sử dụng mã ngôn ngữ {0} ưa thích của bạn, nhưng bản dịch ngôn ngữ đó hoàn thiện chỉ được {1}. Có lẽ bạn có thể giúp chúng tôi cải thiện bản dịch ASF cho ngôn ngữ của bạn?</value>
|
||||
<comment>{0} will be replaced by culture code, such as "en-US", {1} will be replaced by completeness percentage, such as "78.5%"</comment>
|
||||
</data>
|
||||
|
||||
<data name="IdlingGameNotPossible" xml:space="preserve">
|
||||
<value>Việc cày {0} ({1}) tạm thời bị vô hiệu hóa, ASF không thể mở game đó lúc này.</value>
|
||||
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
|
||||
</data>
|
||||
<data name="WarningIdlingGameMismatch" xml:space="preserve">
|
||||
<value>ASF phát hiện ID không khớp với {0} ({1}) và sẽ sử dụng ID {2} thay thế.</value>
|
||||
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name, {2} will be replaced by game's ID (number)</comment>
|
||||
@@ -584,18 +635,36 @@ StackTrace:
|
||||
<value>{0} V{1}</value>
|
||||
<comment>{0} will be replaced by program's name (e.g. "ASF"), {1} will be replaced by program's version (e.g. "1.0.0.0"). This string typically has nothing to translate and you should leave it as it is, unless you need to change the format, e.g. in RTL languages.</comment>
|
||||
</data>
|
||||
|
||||
<data name="BotAccountLocked" xml:space="preserve">
|
||||
<value>Tài khoản này bị khóa, quá trình cày card không chạy!</value>
|
||||
</data>
|
||||
<data name="BotStatusLocked" xml:space="preserve">
|
||||
<value>Bot bị khóa và không thể rớt bất kỳ thẻ thông qua chạy không.</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorFunctionOnlyInHeadlessMode" xml:space="preserve">
|
||||
<value>Chức năng này chỉ có sẵn trong headless mode!</value>
|
||||
</data>
|
||||
<data name="BotOwnedAlready" xml:space="preserve">
|
||||
<value>Đã sở hữu: {0}</value>
|
||||
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="ErrorAccessDenied" xml:space="preserve">
|
||||
<value>Truy cập bị từ chối!</value>
|
||||
</data>
|
||||
<data name="WarningPreReleaseVersion" xml:space="preserve">
|
||||
<value>Bạn đang dùng phiên bản mới hơn phiên bản chính thức mới nhất. Hãy chú ý rằng phiên bản phát hành trước là dành cho người dùng biết cách báo lỗi, xử lý vấn đề và gửi phản hồi - sẽ không có hỗ trợ kỹ thuật.</value>
|
||||
</data>
|
||||
<data name="BotStats" xml:space="preserve">
|
||||
<value>Bộ nhớ đang được sử dụng: {0} MB.</value>
|
||||
<comment>{0} will be replaced by number (in megabytes) of memory being used</comment>
|
||||
</data>
|
||||
<data name="ClearingDiscoveryQueue" xml:space="preserve">
|
||||
<value>Đã xóa hàng đợi khám phá Steam số #{0}...</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="DoneClearingDiscoveryQueue" xml:space="preserve">
|
||||
<value>Đã xóa hàng đợi khám phá Steam số #{0}.</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
|
||||
</root>
|
||||
|
||||
@@ -170,10 +170,6 @@
|
||||
<value>解析 {0} 失败 !</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>无法删除旧的ASF文件,请手动移除{0} 以启动更新功能!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>在 {0} 次尝试后请求失败!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -181,14 +177,19 @@
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>无法检查最新版本 !</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>无法继续更新,因为没有与当前正在运行的版本相关的版本! 无法自动更新到该版本。</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>不能进行更新,因为此版本没有任何资源!</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>收到一个用户输入请求,但进程目前正在以无显示模式运行 !</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>拒绝处理该请求,因为未设置 SteamOwnerID !</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>正在退出...</value>
|
||||
</data>
|
||||
@@ -213,7 +214,7 @@
|
||||
<value>无账号正在运行,即将退出...</value>
|
||||
</data>
|
||||
<data name="RefreshingOurSession" xml:space="preserve">
|
||||
<value>刷新会话 !</value>
|
||||
<value>正在刷新会话!</value>
|
||||
</data>
|
||||
<data name="RejectingTrade" xml:space="preserve">
|
||||
<value>拒绝交易︰ {0}</value>
|
||||
@@ -239,7 +240,8 @@
|
||||
<value>正在检查新版本...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>更新中……如果你喜欢这个项目,可以考虑趁现在进行捐赠:)</value>
|
||||
<value>更新中: {0} ({1} MB)...... 在你等待的时候,可以考虑趁现在进行捐赠!:)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>更新完毕</value>
|
||||
@@ -279,7 +281,10 @@
|
||||
<value>请输入非正式的值 {0}: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>请输入你的IPC主机: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>收到的{0} 为未知值,请报告此值:{1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -288,10 +293,17 @@
|
||||
<value>目前无法同时挂 {0} 个以上的游戏,只有 {1} 里面的前 {0} 个游戏可用!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>IPC 命令响应︰ {0} 及 {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>IPC 服务已就绪 !</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>在 {0} 上的启动 IPC 服务...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>这个帐号已停止运行!</value>
|
||||
</data>
|
||||
@@ -322,10 +334,10 @@
|
||||
<comment>{0} will be replaced by the name of chosen idling algorithm</comment>
|
||||
</data>
|
||||
<data name="Done" xml:space="preserve">
|
||||
<value>完成。</value>
|
||||
<value>完成!</value>
|
||||
</data>
|
||||
<data name="GamesToIdle" xml:space="preserve">
|
||||
<value>共有{0} 个游戏(共计{1} 张卡) 等待挂卡(~还剩{2})...</value>
|
||||
<value>共有 {0} 个游戏(共计 {1} 张卡) 等待挂卡(~还剩{2})...</value>
|
||||
<comment>{0} will be replaced by number of games, {1} will be replaced by number of cards, {2} will be replaced by translated TimeSpan string (such as "1 day, 5 hours and 30 minutes")</comment>
|
||||
</data>
|
||||
<data name="IdlingFinished" xml:space="preserve">
|
||||
@@ -344,13 +356,13 @@
|
||||
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name, {2} will be replaced by number of cards left to idle</comment>
|
||||
</data>
|
||||
<data name="IdlingStopped" xml:space="preserve">
|
||||
<value>挂卡已停止</value>
|
||||
<value>已停止挂卡!</value>
|
||||
</data>
|
||||
<data name="IgnoredStickyPauseEnabled" xml:space="preserve">
|
||||
<value>请求已忽略,因为强制暂停已开启</value>
|
||||
</data>
|
||||
<data name="NothingToIdle" xml:space="preserve">
|
||||
<value>该账户已经无卡可挂</value>
|
||||
<value>该账户已经无卡可挂!</value>
|
||||
</data>
|
||||
<data name="NowIdling" xml:space="preserve">
|
||||
<value>正在挂卡︰{0} ({1})</value>
|
||||
@@ -476,7 +488,7 @@
|
||||
<value>不能发送报价,因为没有定义主权限的用户!</value>
|
||||
</data>
|
||||
<data name="BotLootingNoLootableTypes" xml:space="preserve">
|
||||
<value>你没有设置任何拾取类型!</value>
|
||||
<value>你没有设置任何拾取类型!</value>
|
||||
</data>
|
||||
<data name="BotLootingNowDisabled" xml:space="preserve">
|
||||
<value>拾取现在已禁用!</value>
|
||||
@@ -543,10 +555,6 @@
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>当前帐号正在使用。</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>无法连接到 Steam:{0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>无法登录到 Steam:{0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -655,4 +663,8 @@
|
||||
<value>已完成Steam探索队列 #{0}。</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>{0}/{1} 个BOT已经拥有所有正在被检查的游戏。</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -170,10 +170,6 @@
|
||||
<value>分析 {0} 失敗 !</value>
|
||||
<comment>{0} will be replaced by object's name</comment>
|
||||
</data>
|
||||
<data name="ErrorRemovingOldBinary" xml:space="preserve">
|
||||
<value>無法刪除舊的 ASF 檔案,請依序手動刪除 {0} ,使更新功能正常運作!</value>
|
||||
<comment>{0} will be replaced by file's path</comment>
|
||||
</data>
|
||||
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
|
||||
<value>嘗試請求 {0} 次後失敗!</value>
|
||||
<comment>{0} will be replaced by maximum number of tries</comment>
|
||||
@@ -181,14 +177,19 @@
|
||||
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
|
||||
<value>無法檢查最新版本!</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
|
||||
<value>無法更新。因為當前執行的版本沒有可用的更新檔,程式並未更新到最新版本。</value>
|
||||
</data>
|
||||
<data name="ErrorUpdateNoAssets" xml:space="preserve">
|
||||
<value>無法進行更新,該版本更新檔不存在!</value>
|
||||
</data>
|
||||
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
|
||||
<value>收到一個使用者輸入請求,但已運行在無標頭模式下 !</value>
|
||||
</data>
|
||||
|
||||
<data name="ErrorIPCAccessDenied" xml:space="preserve">
|
||||
<value>因為未設置 SteamOwnerID,無法處理該請求!</value>
|
||||
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
|
||||
</data>
|
||||
<data name="Exiting" xml:space="preserve">
|
||||
<value>正在退出...</value>
|
||||
</data>
|
||||
@@ -239,7 +240,8 @@
|
||||
<value>正在檢查新版本...</value>
|
||||
</data>
|
||||
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
|
||||
<value>正在下載新版本... 等待期間,如果喜歡這個軟體,請考慮捐助ASF! :)</value>
|
||||
<value>正在下載新版本{0} ({1} MB)... 等待期間,如果喜歡這個軟體,請考慮捐助ASF! :)</value>
|
||||
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
|
||||
</data>
|
||||
<data name="UpdateFinished" xml:space="preserve">
|
||||
<value>更新完成 !</value>
|
||||
@@ -279,7 +281,10 @@
|
||||
<value>請輸入未記錄的值 {0}: </value>
|
||||
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
|
||||
<data name="UserInputIPCHost" xml:space="preserve">
|
||||
<value>請輸入您的 IPC 主機: </value>
|
||||
<comment>Please note that this translation should end with space</comment>
|
||||
</data>
|
||||
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
|
||||
<value>收到未知的值為 {0},請回報此問題︰ {1}</value>
|
||||
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
|
||||
@@ -288,10 +293,17 @@
|
||||
<value>不能同時玩超過 {0} 個遊戲,只有 {1} 中的前 {0} 個遊戲可用!</value>
|
||||
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
|
||||
</data>
|
||||
|
||||
|
||||
|
||||
|
||||
<data name="IPCAnswered" xml:space="preserve">
|
||||
<value>IPC 指令回應:{0} 和 {1}</value>
|
||||
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
|
||||
</data>
|
||||
<data name="IPCReady" xml:space="preserve">
|
||||
<value>IPC 伺服器已就緒!</value>
|
||||
</data>
|
||||
<data name="IPCStarting" xml:space="preserve">
|
||||
<value>正在 {0} 上啟動 IPC 伺服器...</value>
|
||||
<comment>{0} will be replaced by IPC hostname</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyStopped" xml:space="preserve">
|
||||
<value>這個 BOT 已經停止了!</value>
|
||||
</data>
|
||||
@@ -405,7 +417,7 @@
|
||||
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by status string, {2} will be replaced by list of granted IDs (numbers), separated by a comma</comment>
|
||||
</data>
|
||||
<data name="BotAlreadyRunning" xml:space="preserve">
|
||||
<value>BOT已在運行中!</value>
|
||||
<value>BOT 已在運行中!</value>
|
||||
</data>
|
||||
<data name="BotAuthenticatorConverting" xml:space="preserve">
|
||||
<value>正在將 .maFile 轉化成 ASF 的文件格式...</value>
|
||||
@@ -543,10 +555,6 @@
|
||||
<data name="BotStatusPlayingNotAvailable" xml:space="preserve">
|
||||
<value>BOT 目前正被使用。</value>
|
||||
</data>
|
||||
<data name="BotUnableToConnect" xml:space="preserve">
|
||||
<value>無法連線至 Steam:{0}</value>
|
||||
<comment>{0} will be replaced by failure reason (string)</comment>
|
||||
</data>
|
||||
<data name="BotUnableToLogin" xml:space="preserve">
|
||||
<value>無法登入到 Steam︰{0}/{1}</value>
|
||||
<comment>{0} will be replaced by failure reason (string), {1} will be replaced by extended failure reason (string)</comment>
|
||||
@@ -655,4 +663,8 @@
|
||||
<value>已完成 Steam 探索佇列 #{0}。</value>
|
||||
<comment>{0} will be replaced by queue number</comment>
|
||||
</data>
|
||||
<data name="BotOwnsOverview" xml:space="preserve">
|
||||
<value>{0}/{1} 的 BOT 已經擁有所有被檢查的遊戲。</value>
|
||||
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -42,14 +42,14 @@ namespace ArchiSteamFarm {
|
||||
private const byte CodeInterval = 30;
|
||||
|
||||
private static readonly char[] CodeCharacters = { '2', '3', '4', '5', '6', '7', '8', '9', 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q', 'R', 'T', 'V', 'W', 'X', 'Y' };
|
||||
private static readonly SemaphoreSlim TimeSemaphore = new SemaphoreSlim(1);
|
||||
private static readonly SemaphoreSlim TimeSemaphore = new SemaphoreSlim(1, 1);
|
||||
|
||||
private static int? SteamTimeDifference;
|
||||
|
||||
// "ERROR" is being used by SteamDesktopAuthenticator
|
||||
internal bool HasCorrectDeviceID => !string.IsNullOrEmpty(DeviceID) && !DeviceID.Equals("ERROR");
|
||||
|
||||
private readonly SemaphoreSlim ConfirmationsSemaphore = new SemaphoreSlim(1);
|
||||
private readonly SemaphoreSlim ConfirmationsSemaphore = new SemaphoreSlim(1, 1);
|
||||
|
||||
#pragma warning disable 649
|
||||
[JsonProperty(PropertyName = "identity_secret", Required = Required.Always)]
|
||||
@@ -70,13 +70,18 @@ namespace ArchiSteamFarm {
|
||||
|
||||
public void Dispose() => ConfirmationsSemaphore.Dispose();
|
||||
|
||||
internal void CorrectDeviceID(string deviceID) {
|
||||
internal bool CorrectDeviceID(string deviceID) {
|
||||
if (string.IsNullOrEmpty(deviceID)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(deviceID));
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(DeviceID) && DeviceID.Equals(deviceID)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DeviceID = deviceID;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal async Task<string> GenerateToken() {
|
||||
|
||||
@@ -23,15 +23,14 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using ArchiSteamFarm.Localization;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class OS {
|
||||
private static readonly PlatformID PlatformID = Environment.OSVersion.Platform;
|
||||
|
||||
internal static void Init(bool headless) {
|
||||
switch (PlatformID) {
|
||||
switch (Environment.OSVersion.Platform) {
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
@@ -46,6 +45,17 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
internal static void UnixSetFileAccessExecutable(string path) {
|
||||
if (!File.Exists(path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Chmod() returns 0 on success, -1 on failure
|
||||
if (NativeMethods.Chmod(path, (int) NativeMethods.UnixExecutePermission) != 0) {
|
||||
ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningFailedWithError, Marshal.GetLastWin32Error()));
|
||||
}
|
||||
}
|
||||
|
||||
private static void DisableQuickEditMode() {
|
||||
// http://stackoverflow.com/questions/30418886/how-and-why-does-quickedit-mode-in-command-prompt-freeze-applications
|
||||
IntPtr consoleHandle = NativeMethods.GetStdHandle(NativeMethods.StandardInputHandle);
|
||||
@@ -66,7 +76,7 @@ namespace ArchiSteamFarm {
|
||||
// This function calls unmanaged API in order to tell Windows OS that it should not enter sleep state while the program is running
|
||||
// If user wishes to enter sleep mode, then he should use ShutdownOnFarmingFinished or manage ASF process with third-party tool or script
|
||||
// More info: https://msdn.microsoft.com/library/windows/desktop/aa373208(v=vs.85).aspx
|
||||
NativeMethods.EExecutionState result = NativeMethods.SetThreadExecutionState(NativeMethods.EExecutionState.AwayModeRequired | NativeMethods.EExecutionState.Continuous | NativeMethods.EExecutionState.SystemRequired);
|
||||
NativeMethods.EExecutionState result = NativeMethods.SetThreadExecutionState(NativeMethods.AwakeExecutionState);
|
||||
|
||||
// SetThreadExecutionState() returns NULL on failure, which is mapped to 0 (EExecutionState.Error) in our case
|
||||
if (result == NativeMethods.EExecutionState.Error) {
|
||||
@@ -75,30 +85,49 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
private static class NativeMethods {
|
||||
internal const EExecutionState AwakeExecutionState = EExecutionState.SystemRequired | EExecutionState.AwayModeRequired | EExecutionState.Continuous;
|
||||
internal const uint EnableQuickEditMode = 0x0040;
|
||||
internal const int StandardInputHandle = -10;
|
||||
internal const sbyte StandardInputHandle = -10;
|
||||
internal const EUnixPermission UnixExecutePermission = EUnixPermission.UserRead | EUnixPermission.UserWrite | EUnixPermission.UserExecute | EUnixPermission.GroupRead | EUnixPermission.GroupExecute | EUnixPermission.OtherRead | EUnixPermission.OtherExecute;
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
||||
[DllImport("libc", EntryPoint = "chmod", SetLastError = true)]
|
||||
internal static extern int Chmod(string path, int mode);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
internal static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
||||
[DllImport("kernel32.dll")]
|
||||
internal static extern IntPtr GetStdHandle(int nStdHandle);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
||||
[DllImport("kernel32.dll")]
|
||||
internal static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
||||
[DllImport("kernel32.dll")]
|
||||
internal static extern EExecutionState SetThreadExecutionState(EExecutionState executionState);
|
||||
|
||||
[Flags]
|
||||
internal enum EExecutionState : uint {
|
||||
Error = 0,
|
||||
SystemRequired = 0x00000001,
|
||||
//DisplayRequired = 0x00000002,
|
||||
//UserPresent = 0x00000004,
|
||||
AwayModeRequired = 0x00000040,
|
||||
Continuous = 0x80000000
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum EUnixPermission {
|
||||
OtherExecute = 0x1,
|
||||
OtherRead = 0x4,
|
||||
GroupExecute = 0x8,
|
||||
GroupRead = 0x20,
|
||||
UserExecute = 0x40,
|
||||
UserWrite = 0x80,
|
||||
UserRead = 0x100
|
||||
|
||||
/*
|
||||
OtherWrite = 0x2
|
||||
GroupWrite = 0x10
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,8 +31,6 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Runtime;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ArchiSteamFarm.Localization;
|
||||
using NLog;
|
||||
@@ -53,7 +51,11 @@ namespace ArchiSteamFarm {
|
||||
internal static WebBrowser WebBrowser { get; private set; }
|
||||
|
||||
private static readonly object ConsoleLock = new object();
|
||||
private static readonly ManualResetEventSlim ShutdownResetEvent = new ManualResetEventSlim(false);
|
||||
|
||||
// We need to keep this one assigned and not calculated on-demand
|
||||
private static readonly string ProcessFileName = Process.GetCurrentProcess().MainModule.FileName;
|
||||
|
||||
private static readonly TaskCompletionSource<bool> ShutdownResetEvent = new TaskCompletionSource<bool>();
|
||||
|
||||
private static bool ShutdownSequenceInitialized;
|
||||
|
||||
@@ -82,33 +84,39 @@ namespace ArchiSteamFarm {
|
||||
switch (userInputType) {
|
||||
case ASF.EUserInputType.DeviceID:
|
||||
Console.Write(Bot.FormatBotResponse(Strings.UserInputDeviceID, botName));
|
||||
result = Console.ReadLine();
|
||||
break;
|
||||
case ASF.EUserInputType.IPCHostname:
|
||||
Console.Write(Bot.FormatBotResponse(Strings.UserInputIPCHost, botName));
|
||||
result = Console.ReadLine();
|
||||
break;
|
||||
case ASF.EUserInputType.Login:
|
||||
Console.Write(Bot.FormatBotResponse(Strings.UserInputSteamLogin, botName));
|
||||
result = Console.ReadLine();
|
||||
break;
|
||||
case ASF.EUserInputType.Password:
|
||||
Console.Write(Bot.FormatBotResponse(Strings.UserInputSteamPassword, botName));
|
||||
result = Utilities.ReadLineMasked();
|
||||
break;
|
||||
case ASF.EUserInputType.SteamGuard:
|
||||
Console.Write(Bot.FormatBotResponse(Strings.UserInputSteamGuard, botName));
|
||||
result = Console.ReadLine();
|
||||
break;
|
||||
case ASF.EUserInputType.SteamParentalPIN:
|
||||
Console.Write(Bot.FormatBotResponse(Strings.UserInputSteamParentalPIN, botName));
|
||||
result = Utilities.ReadLineMasked();
|
||||
break;
|
||||
case ASF.EUserInputType.TwoFactorAuthentication:
|
||||
Console.Write(Bot.FormatBotResponse(Strings.UserInputSteam2FA, botName));
|
||||
result = Console.ReadLine();
|
||||
break;
|
||||
default:
|
||||
ASF.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(userInputType), userInputType));
|
||||
ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(userInputType), userInputType));
|
||||
Console.Write(Bot.FormatBotResponse(string.Format(Strings.UserInputUnknown, userInputType), botName));
|
||||
result = Console.ReadLine();
|
||||
break;
|
||||
}
|
||||
|
||||
result = Console.ReadLine();
|
||||
|
||||
if (!Console.IsOutputRedirected) {
|
||||
Console.Clear(); // For security purposes
|
||||
}
|
||||
@@ -124,24 +132,30 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
string executable = Process.GetCurrentProcess().MainModule.FileName;
|
||||
string executableName = Path.GetFileNameWithoutExtension(executable);
|
||||
// New process might want to start IPC before we in fact close
|
||||
// Ensure that IPC is stopped before Process.Start()
|
||||
IPC.Stop();
|
||||
|
||||
string executableName = Path.GetFileNameWithoutExtension(ProcessFileName);
|
||||
IEnumerable<string> arguments = Environment.GetCommandLineArgs().Skip(executableName.Equals(SharedInfo.AssemblyName) ? 1 : 0);
|
||||
|
||||
try {
|
||||
Process.Start(executable, string.Join(" ", arguments));
|
||||
Process.Start(ProcessFileName, string.Join(" ", arguments));
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
}
|
||||
|
||||
ShutdownResetEvent.Set();
|
||||
// Give new process some time to take over the window (if needed)
|
||||
await Task.Delay(2000).ConfigureAwait(false);
|
||||
|
||||
ShutdownResetEvent.TrySetResult(true);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
private static async Task Init(string[] args) {
|
||||
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
|
||||
TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionHandler;
|
||||
AppDomain.CurrentDomain.ProcessExit += OnProcessExit;
|
||||
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
|
||||
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
|
||||
|
||||
// We must register our logging target as soon as possible
|
||||
Target.Register<SteamTarget>(SteamTarget.TargetName);
|
||||
@@ -151,7 +165,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
private static async Task InitASF(string[] args) {
|
||||
ASF.ArchiLogger.LogGenericInfo("ASF V" + SharedInfo.Version);
|
||||
ASF.ArchiLogger.LogGenericInfo("ASF V" + SharedInfo.Version + " (" + SharedInfo.ModuleVersion + ")");
|
||||
|
||||
await InitGlobalConfigAndLanguage().ConfigureAwait(false);
|
||||
await InitGlobalDatabaseAndServices().ConfigureAwait(false);
|
||||
@@ -180,9 +194,7 @@ namespace ArchiSteamFarm {
|
||||
ParsePostInitArgs(args);
|
||||
}
|
||||
|
||||
if (!Debugging.IsDebugBuild) {
|
||||
await ASF.CheckForUpdate().ConfigureAwait(false);
|
||||
}
|
||||
await ASF.CheckForUpdate().ConfigureAwait(false);
|
||||
|
||||
await ASF.InitBots().ConfigureAwait(false);
|
||||
ASF.InitEvents();
|
||||
@@ -229,8 +241,8 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GCSettings.IsServerGC) {
|
||||
Hacks.Init();
|
||||
if (GlobalConfig.BackgroundGCPeriod > 0) {
|
||||
Hacks.EnableBackgroundGC(GlobalConfig.BackgroundGCPeriod);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(GlobalConfig.CurrentCulture)) {
|
||||
@@ -290,7 +302,7 @@ namespace ArchiSteamFarm {
|
||||
if (!File.Exists(globalDatabaseFile)) {
|
||||
ASF.ArchiLogger.LogGenericInfo(Strings.Welcome);
|
||||
ASF.ArchiLogger.LogGenericWarning(Strings.WarningPrivacyPolicy);
|
||||
await Task.Delay(15 * 1000).ConfigureAwait(false);
|
||||
await Task.Delay(10 * 1000).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
GlobalDatabase = GlobalDatabase.Load(globalDatabaseFile);
|
||||
@@ -301,12 +313,11 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
ArchiWebHandler.Init();
|
||||
IPC.Initialize(GlobalConfig.IPCHost, GlobalConfig.IPCPort);
|
||||
OS.Init(GlobalConfig.Headless);
|
||||
WebBrowser.Init();
|
||||
|
||||
WebBrowser = new WebBrowser(ASF.ArchiLogger);
|
||||
WebBrowser = new WebBrowser(ASF.ArchiLogger, true);
|
||||
}
|
||||
|
||||
private static async Task<bool> InitShutdownSequence() {
|
||||
@@ -316,38 +327,60 @@ namespace ArchiSteamFarm {
|
||||
|
||||
ShutdownSequenceInitialized = true;
|
||||
|
||||
IPC.Stop();
|
||||
if (Bot.Bots.Count > 0) {
|
||||
IEnumerable<Task> tasks = Bot.Bots.Values.Select(bot => Task.Run(() => bot.Stop(false)));
|
||||
|
||||
if (Bot.Bots.Count == 0) {
|
||||
return true;
|
||||
}
|
||||
switch (GlobalConfig.OptimizationMode) {
|
||||
case GlobalConfig.EOptimizationMode.MinMemoryUsage:
|
||||
foreach (Task task in tasks) {
|
||||
await Task.WhenAny(task, Task.Delay(WebBrowser.MaxTries * 1000)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
IEnumerable<Task> tasks = Bot.Bots.Values.Select(bot => Task.Run(() => bot.Stop(false)));
|
||||
break;
|
||||
default:
|
||||
await Task.WhenAny(Task.WhenAll(tasks), Task.Delay(Bot.Bots.Count * WebBrowser.MaxTries * 1000)).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (GlobalConfig.OptimizationMode) {
|
||||
case GlobalConfig.EOptimizationMode.MinMemoryUsage:
|
||||
foreach (Task task in tasks) {
|
||||
await Task.WhenAny(task, Task.Delay(WebBrowser.MaxRetries * 1000)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
await Task.WhenAny(Task.WhenAll(tasks), Task.Delay(Bot.Bots.Count * WebBrowser.MaxRetries * 1000)).ConfigureAwait(false);
|
||||
break;
|
||||
// Extra second for Steam requests to go through
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
LogManager.Flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void Main(string[] args) {
|
||||
Init(args).Wait();
|
||||
private static async Task Main(string[] args) {
|
||||
// Initialize
|
||||
await Init(args).ConfigureAwait(false);
|
||||
|
||||
// Wait for signal to shutdown
|
||||
ShutdownResetEvent.Wait();
|
||||
// Wait for shutdown event
|
||||
await ShutdownResetEvent.Task.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// We got a signal to shutdown
|
||||
Exit().Wait();
|
||||
private static void OnProcessExit(object sender, EventArgs e) => IPC.Stop();
|
||||
|
||||
private static async void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) {
|
||||
if (e?.ExceptionObject == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(e) + " || " + nameof(e.ExceptionObject));
|
||||
return;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogFatalException((Exception) e.ExceptionObject);
|
||||
await Exit(1).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) {
|
||||
if (e?.Exception == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(e) + " || " + nameof(e.Exception));
|
||||
return;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogFatalException(e.Exception);
|
||||
|
||||
// Normally we should abort the application here, but many tasks are in fact failing in SK2 code which we can't easily fix
|
||||
// Thanks Valve.
|
||||
e.SetObserved();
|
||||
}
|
||||
|
||||
private static void ParsePostInitArgs(IEnumerable<string> args) {
|
||||
@@ -402,30 +435,7 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
ShutdownResetEvent.Set();
|
||||
}
|
||||
|
||||
private static async void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e) {
|
||||
if (e?.ExceptionObject == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(e) + " || " + nameof(e.ExceptionObject));
|
||||
return;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogFatalException((Exception) e.ExceptionObject);
|
||||
await Exit(1).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static void UnobservedTaskExceptionHandler(object sender, UnobservedTaskExceptionEventArgs e) {
|
||||
if (e?.Exception == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(e) + " || " + nameof(e.Exception));
|
||||
return;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogFatalException(e.Exception);
|
||||
|
||||
// Normally we should abort the application here, but many tasks are in fact failing in SK2 code which we can't easily fix
|
||||
// Thanks Valve.
|
||||
e.SetObserved();
|
||||
ShutdownResetEvent.TrySetResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
68
ArchiSteamFarm/ServerRecordEndPoint.cs
Normal file
68
ArchiSteamFarm/ServerRecordEndPoint.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2017 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using SteamKit2;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class ServerRecordEndPoint {
|
||||
[JsonProperty(Required = Required.Always)]
|
||||
internal readonly string Host;
|
||||
|
||||
[JsonProperty(Required = Required.Always)]
|
||||
internal readonly ushort Port;
|
||||
|
||||
[JsonProperty(Required = Required.Always)]
|
||||
internal readonly ProtocolTypes ProtocolTypes;
|
||||
|
||||
internal ServerRecordEndPoint(string host, ushort port, ProtocolTypes protocolTypes) {
|
||||
if (string.IsNullOrEmpty(host) || (port == 0) || (protocolTypes == 0)) {
|
||||
throw new ArgumentNullException(nameof(host) + " || " + nameof(port) + " || " + nameof(protocolTypes));
|
||||
}
|
||||
|
||||
Host = host;
|
||||
Port = port;
|
||||
ProtocolTypes = protocolTypes;
|
||||
}
|
||||
|
||||
private ServerRecordEndPoint() { }
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
if (ReferenceEquals(null, obj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(this, obj)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return obj is ServerRecordEndPoint serverRecord && Equals(serverRecord);
|
||||
}
|
||||
|
||||
public override int GetHashCode() => (Host, Port, ProtocolTypes).GetHashCode();
|
||||
|
||||
private bool Equals(ServerRecordEndPoint other) => string.Equals(Host, other.Host) && (Port == other.Port) && (ProtocolTypes == other.ProtocolTypes);
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ namespace ArchiSteamFarm {
|
||||
internal const string UpdateDirectory = "_old";
|
||||
internal const string VersionFile = AssemblyName + ".version";
|
||||
|
||||
internal static readonly Version Version = Assembly.GetEntryAssembly().GetName().Version;
|
||||
internal static Guid ModuleVersion => Assembly.GetEntryAssembly().ManifestModule.ModuleVersionId;
|
||||
internal static Version Version => Assembly.GetEntryAssembly().GetName().Version;
|
||||
}
|
||||
}
|
||||
@@ -28,19 +28,20 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ArchiSteamFarm.JSON;
|
||||
using Newtonsoft.Json;
|
||||
using SteamKit2;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class Statistics : IDisposable {
|
||||
private const byte MinAnnouncementCheckTTL = 6; // Minimum amount of hours we must wait before checking eligibility for Announcement, should be lower than MinPersonaStateTTL
|
||||
private const byte MinCardsCount = 100; // Minimum amount of cards to be eligible for public listing
|
||||
private const byte MinHeartBeatTTL = 10; // Minimum amount of minutes we must wait before sending next HeartBeat
|
||||
private const byte MinItemsCount = 100; // Minimum amount of items to be eligible for public listing
|
||||
private const byte MinPersonaStateTTL = 8; // Minimum amount of hours we must wait before requesting persona state update
|
||||
|
||||
private const string URL = "https://" + SharedInfo.StatisticsServer;
|
||||
|
||||
private readonly Bot Bot;
|
||||
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);
|
||||
private readonly SemaphoreSlim RequestsSemaphore = new SemaphoreSlim(1, 1);
|
||||
|
||||
private DateTime LastAnnouncementCheck = DateTime.MinValue;
|
||||
private DateTime LastHeartBeat = DateTime.MinValue;
|
||||
private DateTime LastPersonaStateRequest = DateTime.MinValue;
|
||||
@@ -48,7 +49,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
internal Statistics(Bot bot) => Bot = bot ?? throw new ArgumentNullException(nameof(bot));
|
||||
|
||||
public void Dispose() => Semaphore.Dispose();
|
||||
public void Dispose() => RequestsSemaphore.Dispose();
|
||||
|
||||
internal async Task OnHeartBeat() {
|
||||
// Request persona update if needed
|
||||
@@ -61,7 +62,7 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
await Semaphore.WaitAsync().ConfigureAwait(false);
|
||||
await RequestsSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
try {
|
||||
if (!ShouldSendHeartBeats || (DateTime.UtcNow < LastHeartBeat.AddMinutes(MinHeartBeatTTL))) {
|
||||
@@ -79,7 +80,7 @@ namespace ArchiSteamFarm {
|
||||
LastHeartBeat = DateTime.UtcNow;
|
||||
}
|
||||
} finally {
|
||||
Semaphore.Release();
|
||||
RequestsSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +98,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
// Don't announce if we don't meet conditions
|
||||
string tradeToken;
|
||||
if (!Bot.HasMobileAuthenticator || !Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.SteamTradeMatcher) || !await Bot.ArchiWebHandler.HasValidApiKey().ConfigureAwait(false) || !await Bot.ArchiWebHandler.HasPublicInventory().ConfigureAwait(false) || string.IsNullOrEmpty(tradeToken = await Bot.ArchiWebHandler.GetTradeToken().ConfigureAwait(false))) {
|
||||
if (!Bot.HasMobileAuthenticator || !Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.SteamTradeMatcher) || (Bot.BotConfig.MatchableTypes.Count == 0) || !await Bot.ArchiWebHandler.HasValidApiKey().ConfigureAwait(false) || !await Bot.ArchiWebHandler.HasPublicInventory().ConfigureAwait(false) || string.IsNullOrEmpty(tradeToken = await Bot.ArchiWebHandler.GetTradeToken().ConfigureAwait(false))) {
|
||||
LastAnnouncementCheck = DateTime.UtcNow;
|
||||
ShouldSendHeartBeats = false;
|
||||
return;
|
||||
@@ -113,16 +114,14 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
bool matchEverything = Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchEverything);
|
||||
|
||||
await Semaphore.WaitAsync().ConfigureAwait(false);
|
||||
await RequestsSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
try {
|
||||
if (DateTime.UtcNow < LastAnnouncementCheck.AddHours(MinAnnouncementCheckTTL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
HashSet<Steam.Item> inventory = await Bot.ArchiWebHandler.GetMySteamInventory(true, new HashSet<Steam.Item.EType> { Steam.Item.EType.TradingCard }).ConfigureAwait(false);
|
||||
HashSet<Steam.Item> inventory = await Bot.ArchiWebHandler.GetMySteamInventory(true, Bot.BotConfig.MatchableTypes).ConfigureAwait(false);
|
||||
|
||||
// This is actually inventory failure, so we'll stop sending heartbeats but not record it as valid check
|
||||
if (inventory == null) {
|
||||
@@ -131,21 +130,22 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
// This is actual inventory
|
||||
if (inventory.Count < MinCardsCount) {
|
||||
if (inventory.Count < MinItemsCount) {
|
||||
LastAnnouncementCheck = DateTime.UtcNow;
|
||||
ShouldSendHeartBeats = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const string request = URL + "/api/Announce";
|
||||
Dictionary<string, string> data = new Dictionary<string, string>(7) {
|
||||
Dictionary<string, string> data = new Dictionary<string, string>(8) {
|
||||
{ "SteamID", Bot.SteamID.ToString() },
|
||||
{ "Guid", Program.GlobalDatabase.Guid.ToString("N") },
|
||||
{ "Nickname", nickname },
|
||||
{ "AvatarHash", avatarHash },
|
||||
{ "MatchEverything", matchEverything ? "1" : "0" },
|
||||
{ "MatchableTypes", JsonConvert.SerializeObject(Bot.BotConfig.MatchableTypes) },
|
||||
{ "MatchEverything", Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchEverything) ? "1" : "0" },
|
||||
{ "TradeToken", tradeToken },
|
||||
{ "CardsCount", inventory.Count.ToString() }
|
||||
{ "ItemsCount", inventory.Count.ToString() }
|
||||
};
|
||||
|
||||
// We don't need retry logic here
|
||||
@@ -154,7 +154,7 @@ namespace ArchiSteamFarm {
|
||||
ShouldSendHeartBeats = true;
|
||||
}
|
||||
} finally {
|
||||
Semaphore.Release();
|
||||
RequestsSemaphore.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,8 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
base.Write(logEvent);
|
||||
|
||||
if (SteamID == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -32,12 +32,12 @@ using ArchiSteamFarm.Localization;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class Trading : IDisposable {
|
||||
internal const byte MaxItemsPerTrade = 150; // This is due to limit on POST size in WebBrowser
|
||||
internal const byte MaxItemsPerTrade = byte.MaxValue; // This is due to limit on POST size in WebBrowser
|
||||
internal const byte MaxTradesPerAccount = 5; // This is limit introduced by Valve
|
||||
|
||||
private readonly Bot Bot;
|
||||
private readonly ConcurrentHashSet<ulong> IgnoredTrades = new ConcurrentHashSet<ulong>();
|
||||
private readonly SemaphoreSlim TradesSemaphore = new SemaphoreSlim(1);
|
||||
private readonly SemaphoreSlim TradesSemaphore = new SemaphoreSlim(1, 1);
|
||||
|
||||
private bool ParsingScheduled;
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
public void Dispose() => TradesSemaphore.Dispose();
|
||||
|
||||
internal void OnDisconnected() => IgnoredTrades.ClearAndTrim();
|
||||
internal void OnDisconnected() => IgnoredTrades.Clear();
|
||||
|
||||
internal async Task OnNewTrade() {
|
||||
// We aim to have a maximum of 2 tasks, one already parsing, and one waiting in the queue
|
||||
@@ -71,6 +71,76 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsTradeNeutralOrBetter(HashSet<Steam.Item> inventory, HashSet<Steam.Item> itemsToGive, HashSet<Steam.Item> itemsToReceive) {
|
||||
if ((inventory == null) || (inventory.Count == 0) || (itemsToGive == null) || (itemsToGive.Count == 0) || (itemsToReceive == null) || (itemsToReceive.Count == 0)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(inventory) + " || " + nameof(itemsToGive) + " || " + nameof(itemsToReceive));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now let's create a map which maps items to their amount in our EQ
|
||||
// This has to be done as we might have multiple items of given ClassID with multiple amounts
|
||||
Dictionary<ulong, uint> itemAmounts = new Dictionary<ulong, uint>();
|
||||
foreach (Steam.Item item in inventory) {
|
||||
if (itemAmounts.TryGetValue(item.ClassID, out uint amount)) {
|
||||
itemAmounts[item.ClassID] = amount + item.Amount;
|
||||
} else {
|
||||
itemAmounts[item.ClassID] = item.Amount;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate our value of items to give on per-game basis
|
||||
Dictionary<(Steam.Item.EType Type, uint AppID), List<uint>> itemAmountToGivePerGame = new Dictionary<(Steam.Item.EType Type, uint AppID), List<uint>>();
|
||||
Dictionary<ulong, uint> itemAmountsToGive = new Dictionary<ulong, uint>(itemAmounts);
|
||||
foreach (Steam.Item item in itemsToGive) {
|
||||
if (!itemAmountToGivePerGame.TryGetValue((item.Type, item.RealAppID), out List<uint> amountsToGive)) {
|
||||
amountsToGive = new List<uint>();
|
||||
itemAmountToGivePerGame[(item.Type, item.RealAppID)] = amountsToGive;
|
||||
}
|
||||
|
||||
if (!itemAmountsToGive.TryGetValue(item.ClassID, out uint amount)) {
|
||||
amountsToGive.Add(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
amountsToGive.Add(amount);
|
||||
itemAmountsToGive[item.ClassID] = amount - 1; // We're giving one, so we have one less
|
||||
}
|
||||
|
||||
// Sort all the lists of amounts to give on per-game basis ascending
|
||||
foreach (List<uint> amountsToGive in itemAmountToGivePerGame.Values) {
|
||||
amountsToGive.Sort();
|
||||
}
|
||||
|
||||
// Calculate our value of items to receive on per-game basis
|
||||
Dictionary<(Steam.Item.EType Type, uint AppID), List<uint>> itemAmountToReceivePerGame = new Dictionary<(Steam.Item.EType Type, uint AppID), List<uint>>();
|
||||
Dictionary<ulong, uint> itemAmountsToReceive = new Dictionary<ulong, uint>(itemAmounts);
|
||||
foreach (Steam.Item item in itemsToReceive) {
|
||||
if (!itemAmountToReceivePerGame.TryGetValue((item.Type, item.RealAppID), out List<uint> amountsToReceive)) {
|
||||
amountsToReceive = new List<uint>();
|
||||
itemAmountToReceivePerGame[(item.Type, item.RealAppID)] = amountsToReceive;
|
||||
}
|
||||
|
||||
if (!itemAmountsToReceive.TryGetValue(item.ClassID, out uint amount)) {
|
||||
amountsToReceive.Add(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
amountsToReceive.Add(amount);
|
||||
itemAmountsToReceive[item.ClassID] = amount + 1; // We're getting one, so we have one more
|
||||
}
|
||||
|
||||
// Sort all the lists of amounts to receive on per-game basis ascending
|
||||
foreach (List<uint> amountsToReceive in itemAmountToReceivePerGame.Values) {
|
||||
amountsToReceive.Sort();
|
||||
}
|
||||
|
||||
// Calculate final neutrality result
|
||||
// This is quite complex operation of taking minimum difference from all differences on per-game basis
|
||||
// When calculating per-game difference, we sum only amounts at proper indexes, because user might be overpaying
|
||||
int difference = itemAmountToGivePerGame.Min(kv => kv.Value.Select((t, i) => (int) (t - itemAmountToReceivePerGame[kv.Key][i])).Sum());
|
||||
return difference > 0;
|
||||
}
|
||||
|
||||
private async Task ParseActiveTrades() {
|
||||
HashSet<Steam.TradeOffer> tradeOffers = await Bot.ArchiWebHandler.GetActiveTradeOffers().ConfigureAwait(false);
|
||||
if ((tradeOffers == null) || (tradeOffers.Count == 0)) {
|
||||
@@ -143,9 +213,7 @@ namespace ArchiSteamFarm {
|
||||
case ParseTradeResult.EResult.RejectedTemporarily:
|
||||
if (result.Result == ParseTradeResult.EResult.RejectedPermanently) {
|
||||
if (Bot.BotConfig.IsBotAccount) {
|
||||
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.RejectingTrade, tradeOffer.TradeOfferID));
|
||||
await Bot.ArchiWebHandler.DeclineTradeOffer(tradeOffer.TradeOfferID).ConfigureAwait(false);
|
||||
break;
|
||||
goto case ParseTradeResult.EResult.RejectedAndBlacklisted;
|
||||
}
|
||||
|
||||
IgnoredTrades.Add(tradeOffer.TradeOfferID);
|
||||
@@ -153,8 +221,12 @@ namespace ArchiSteamFarm {
|
||||
|
||||
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.IgnoringTrade, tradeOffer.TradeOfferID));
|
||||
break;
|
||||
case ParseTradeResult.EResult.RejectedAndBlacklisted:
|
||||
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.RejectingTrade, tradeOffer.TradeOfferID));
|
||||
await Bot.ArchiWebHandler.DeclineTradeOffer(tradeOffer.TradeOfferID).ConfigureAwait(false);
|
||||
break;
|
||||
default:
|
||||
Bot.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, result.Result));
|
||||
Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(result.Result), result.Result));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -173,9 +245,9 @@ namespace ArchiSteamFarm {
|
||||
return new ParseTradeResult(tradeOffer.TradeOfferID, tradeOffer.ItemsToGive.Count > 0 ? ParseTradeResult.EResult.AcceptedWithItemLose : ParseTradeResult.EResult.AcceptedWithoutItemLose);
|
||||
}
|
||||
|
||||
// Always deny trades from blacklistem steamIDs
|
||||
// Always deny trades from blacklisted steamIDs
|
||||
if (Bot.IsBlacklistedFromTrades(tradeOffer.OtherSteamID64)) {
|
||||
return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedPermanently);
|
||||
return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedAndBlacklisted);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,8 +286,8 @@ namespace ArchiSteamFarm {
|
||||
return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedPermanently);
|
||||
}
|
||||
|
||||
// Decline trade if we're losing anything but steam cards, or if it's non-dupes trade
|
||||
if (!tradeOffer.IsSteamCardsRequest() || !tradeOffer.IsFairTypesExchange()) {
|
||||
// Decline trade if it's not fair games/types exchange or if we're requested to handle any not-accepted item type
|
||||
if (!tradeOffer.IsFairTypesExchange() || !tradeOffer.IsValidSteamItemsRequest(Bot.BotConfig.MatchableTypes)) {
|
||||
return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedPermanently);
|
||||
}
|
||||
|
||||
@@ -231,7 +303,7 @@ namespace ArchiSteamFarm {
|
||||
// If user has a trade hold, we add extra logic
|
||||
if (holdDuration.Value > 0) {
|
||||
// If trade hold duration exceeds our max, or user asks for cards with short lifespan, reject the trade
|
||||
if ((holdDuration.Value > Program.GlobalConfig.MaxTradeHoldDuration) || tradeOffer.ItemsToGive.Any(item => GlobalConfig.GlobalBlacklist.Contains(item.RealAppID))) {
|
||||
if ((holdDuration.Value > Program.GlobalConfig.MaxTradeHoldDuration) || tradeOffer.ItemsToGive.Any(item => GlobalConfig.SalesBlacklist.Contains(item.RealAppID))) {
|
||||
return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedPermanently);
|
||||
}
|
||||
}
|
||||
@@ -241,82 +313,27 @@ namespace ArchiSteamFarm {
|
||||
return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.AcceptedWithItemLose);
|
||||
}
|
||||
|
||||
// Get appIDs we're interested in
|
||||
HashSet<uint> appIDs = new HashSet<uint>(tradeOffer.ItemsToGive.Select(item => item.RealAppID));
|
||||
// Get appIDs/types we're interested in
|
||||
HashSet<uint> appIDs = new HashSet<uint>();
|
||||
HashSet<Steam.Item.EType> types = new HashSet<Steam.Item.EType>();
|
||||
|
||||
foreach (Steam.Item item in tradeOffer.ItemsToGive) {
|
||||
appIDs.Add(item.RealAppID);
|
||||
types.Add(item.Type);
|
||||
}
|
||||
|
||||
// Now check if it's worth for us to do the trade
|
||||
HashSet<Steam.Item> inventory = await Bot.ArchiWebHandler.GetMySteamInventory(false, new HashSet<Steam.Item.EType> { Steam.Item.EType.TradingCard }, appIDs).ConfigureAwait(false);
|
||||
HashSet<Steam.Item> inventory = await Bot.ArchiWebHandler.GetMySteamInventory(false, types, appIDs).ConfigureAwait(false);
|
||||
if ((inventory == null) || (inventory.Count == 0)) {
|
||||
// If we can't check our inventory when not using MatchEverything, this is a temporary failure
|
||||
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsEmpty, nameof(inventory)));
|
||||
return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedTemporarily);
|
||||
}
|
||||
|
||||
// Now let's create a map which maps items to their amount in our EQ
|
||||
// This has to be done as we might have multiple items of given ClassID with multiple amounts
|
||||
Dictionary<ulong, uint> itemAmounts = new Dictionary<ulong, uint>();
|
||||
foreach (Steam.Item item in inventory) {
|
||||
if (itemAmounts.TryGetValue(item.ClassID, out uint amount)) {
|
||||
itemAmounts[item.ClassID] = amount + item.Amount;
|
||||
} else {
|
||||
itemAmounts[item.ClassID] = item.Amount;
|
||||
}
|
||||
}
|
||||
bool accept = IsTradeNeutralOrBetter(inventory, tradeOffer.ItemsToGive, tradeOffer.ItemsToReceive);
|
||||
|
||||
// Calculate our value of items to give on per-game basis
|
||||
Dictionary<uint, List<uint>> itemAmountToGivePerGame = new Dictionary<uint, List<uint>>(appIDs.Count);
|
||||
Dictionary<ulong, uint> itemAmountsToGive = new Dictionary<ulong, uint>(itemAmounts);
|
||||
foreach (Steam.Item item in tradeOffer.ItemsToGive) {
|
||||
if (!itemAmountToGivePerGame.TryGetValue(item.RealAppID, out List<uint> amountsToGive)) {
|
||||
amountsToGive = new List<uint>();
|
||||
itemAmountToGivePerGame[item.RealAppID] = amountsToGive;
|
||||
}
|
||||
|
||||
if (!itemAmountsToGive.TryGetValue(item.ClassID, out uint amount)) {
|
||||
amountsToGive.Add(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
amountsToGive.Add(amount);
|
||||
itemAmountsToGive[item.ClassID] = amount - 1; // We're giving one, so we have one less
|
||||
}
|
||||
|
||||
// Sort all the lists of amounts to give on per-game basis ascending
|
||||
foreach (List<uint> amountsToGive in itemAmountToGivePerGame.Values) {
|
||||
amountsToGive.Sort();
|
||||
}
|
||||
|
||||
// Calculate our value of items to receive on per-game basis
|
||||
Dictionary<uint, List<uint>> itemAmountToReceivePerGame = new Dictionary<uint, List<uint>>(appIDs.Count);
|
||||
Dictionary<ulong, uint> itemAmountsToReceive = new Dictionary<ulong, uint>(itemAmounts);
|
||||
foreach (Steam.Item item in tradeOffer.ItemsToReceive) {
|
||||
if (!itemAmountToReceivePerGame.TryGetValue(item.RealAppID, out List<uint> amountsToReceive)) {
|
||||
amountsToReceive = new List<uint>();
|
||||
itemAmountToReceivePerGame[item.RealAppID] = amountsToReceive;
|
||||
}
|
||||
|
||||
if (!itemAmountsToReceive.TryGetValue(item.ClassID, out uint amount)) {
|
||||
amountsToReceive.Add(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
amountsToReceive.Add(amount);
|
||||
itemAmountsToReceive[item.ClassID] = amount + 1; // We're getting one, so we have one more
|
||||
}
|
||||
|
||||
// Sort all the lists of amounts to receive on per-game basis ascending
|
||||
foreach (List<uint> amountsToReceive in itemAmountToReceivePerGame.Values) {
|
||||
amountsToReceive.Sort();
|
||||
}
|
||||
|
||||
// Calculate final neutrality result
|
||||
// This is quite complex operation of taking minimum difference from all differences on per-game basis
|
||||
// When calculating per-game difference, we sum only amounts at proper indexes, because user might be overpaying
|
||||
int difference = itemAmountToGivePerGame.Min(kv => kv.Value.Select((t, i) => (int) (t - itemAmountToReceivePerGame[kv.Key][i])).Sum());
|
||||
|
||||
// Trade is neutral+ for us if the difference is greater than 0
|
||||
// If not, we assume that the trade might be good for us in the future, unless we're bot account where we assume that inventory doesn't change
|
||||
return new ParseTradeResult(tradeOffer.TradeOfferID, difference > 0 ? ParseTradeResult.EResult.AcceptedWithItemLose : (Bot.BotConfig.IsBotAccount ? ParseTradeResult.EResult.RejectedPermanently : ParseTradeResult.EResult.RejectedTemporarily));
|
||||
// Even if trade is not neutral+ for us right now, it might be in the future, unless we're bot account where we assume that inventory doesn't change
|
||||
return new ParseTradeResult(tradeOffer.TradeOfferID, accept ? ParseTradeResult.EResult.AcceptedWithItemLose : (Bot.BotConfig.IsBotAccount ? ParseTradeResult.EResult.RejectedPermanently : ParseTradeResult.EResult.RejectedTemporarily));
|
||||
}
|
||||
|
||||
private sealed class ParseTradeResult {
|
||||
@@ -338,7 +355,8 @@ namespace ArchiSteamFarm {
|
||||
AcceptedWithItemLose,
|
||||
AcceptedWithoutItemLose,
|
||||
RejectedTemporarily,
|
||||
RejectedPermanently
|
||||
RejectedPermanently,
|
||||
RejectedAndBlacklisted
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Humanizer;
|
||||
|
||||
@@ -40,16 +41,6 @@ namespace ArchiSteamFarm {
|
||||
[SuppressMessage("ReSharper", "UnusedParameter.Global")]
|
||||
internal static void Forget(this object obj) { }
|
||||
|
||||
internal static string GetArgsAsString(this string[] args, byte argsToSkip = 1) {
|
||||
if ((args == null) || (args.Length < argsToSkip)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(args));
|
||||
return null;
|
||||
}
|
||||
|
||||
string result = string.Join(" ", args.GetArgs(argsToSkip));
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static string GetCookieValue(this CookieContainer cookieContainer, string url, string name) {
|
||||
if ((cookieContainer == null) || string.IsNullOrEmpty(url) || string.IsNullOrEmpty(name)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(cookieContainer) + " || " + nameof(url) + " || " + nameof(name));
|
||||
@@ -95,22 +86,59 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
internal static void StartBackgroundAction(Action action) {
|
||||
internal static string ReadLineMasked(char mask = '*') {
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
ConsoleKeyInfo keyInfo;
|
||||
while ((keyInfo = Console.ReadKey(true)).Key != ConsoleKey.Enter) {
|
||||
if (!char.IsControl(keyInfo.KeyChar)) {
|
||||
result.Append(keyInfo.KeyChar);
|
||||
Console.Write(mask);
|
||||
} else if ((keyInfo.Key == ConsoleKey.Backspace) && (result.Length > 0)) {
|
||||
result.Remove(result.Length - 1, 1);
|
||||
|
||||
if (Console.CursorLeft == 0) {
|
||||
Console.SetCursorPosition(Console.BufferWidth - 1, Console.CursorTop - 1);
|
||||
Console.Write(' ');
|
||||
Console.SetCursorPosition(Console.BufferWidth - 1, Console.CursorTop - 1);
|
||||
} else {
|
||||
Console.Write("\b \b");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
internal static void StartBackgroundAction(Action action, bool longRunning = true) {
|
||||
if (action == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(action));
|
||||
return;
|
||||
}
|
||||
|
||||
Task.Factory.StartNew(action, TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning).Forget();
|
||||
TaskCreationOptions options = TaskCreationOptions.DenyChildAttach;
|
||||
|
||||
if (longRunning) {
|
||||
options |= TaskCreationOptions.LongRunning;
|
||||
}
|
||||
|
||||
Task.Factory.StartNew(action, options).Forget();
|
||||
}
|
||||
|
||||
internal static void StartBackgroundFunction(Func<Task> function) {
|
||||
internal static void StartBackgroundFunction(Func<Task> function, bool longRunning = true) {
|
||||
if (function == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(function));
|
||||
return;
|
||||
}
|
||||
|
||||
Task.Factory.StartNew(function, TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning).Forget();
|
||||
TaskCreationOptions options = TaskCreationOptions.DenyChildAttach;
|
||||
|
||||
if (longRunning) {
|
||||
options |= TaskCreationOptions.LongRunning;
|
||||
}
|
||||
|
||||
Task.Factory.StartNew(function, options).Forget();
|
||||
}
|
||||
|
||||
internal static IEnumerable<T> ToEnumerable<T>(this T item) where T : struct {
|
||||
@@ -118,22 +146,5 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
internal static string ToHumanReadable(this TimeSpan timeSpan) => timeSpan.Humanize(3);
|
||||
|
||||
private static string[] GetArgs(this string[] args, byte argsToSkip = 1) {
|
||||
if ((args == null) || (args.Length < argsToSkip)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(args));
|
||||
return null;
|
||||
}
|
||||
|
||||
byte argsToCopy = (byte) (args.Length - argsToSkip);
|
||||
|
||||
string[] result = new string[argsToCopy];
|
||||
|
||||
if (argsToCopy > 0) {
|
||||
Array.Copy(args, argsToSkip, result, 0, argsToCopy);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,33 +34,39 @@ using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class WebBrowser {
|
||||
internal const byte MaxRetries = 5; // Defines maximum number of retries, UrlRequest() does not handle retry by itself (it's app responsibility)
|
||||
internal sealed class WebBrowser : IDisposable {
|
||||
internal const byte MaxTries = 5; // Defines maximum number of recommended tries for a single request
|
||||
|
||||
private const byte MaxConnections = ServicePointManager.DefaultNonPersistentConnectionLimit; // Defines maximum number of connections per ServicePoint. Be careful, as it also defines maximum number of sockets in CLOSE_WAIT state
|
||||
private const byte MaxIdleTime = 15; // In seconds, how long socket is allowed to stay in CLOSE_WAIT state after there are no connections to it
|
||||
private const byte ExtendedTimeoutMultiplier = 10; // Defines multiplier of timeout for WebBrowsers dealing with huge data (ASF update)
|
||||
private const byte MaxConnections = 10; // Defines maximum number of connections per ServicePoint. Be careful, as it also defines maximum number of sockets in CLOSE_WAIT state
|
||||
private const byte MaxIdleTime = 15; // Defines in seconds, how long socket is allowed to stay in CLOSE_WAIT state after there are no connections to it
|
||||
|
||||
internal readonly CookieContainer CookieContainer = new CookieContainer();
|
||||
|
||||
internal TimeSpan Timeout => HttpClient.Timeout;
|
||||
|
||||
private readonly ArchiLogger ArchiLogger;
|
||||
private readonly HttpClient HttpClient;
|
||||
|
||||
internal WebBrowser(ArchiLogger archiLogger) {
|
||||
internal WebBrowser(ArchiLogger archiLogger, bool extendedTimeout = false) {
|
||||
ArchiLogger = archiLogger ?? throw new ArgumentNullException(nameof(archiLogger));
|
||||
|
||||
HttpClientHandler httpClientHandler = new HttpClientHandler {
|
||||
AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,
|
||||
CookieContainer = CookieContainer
|
||||
CookieContainer = CookieContainer,
|
||||
MaxConnectionsPerServer = MaxConnections
|
||||
};
|
||||
|
||||
HttpClient = new HttpClient(httpClientHandler) {
|
||||
Timeout = TimeSpan.FromSeconds(Program.GlobalConfig.ConnectionTimeout)
|
||||
Timeout = TimeSpan.FromSeconds(extendedTimeout ? ExtendedTimeoutMultiplier * Program.GlobalConfig.ConnectionTimeout : Program.GlobalConfig.ConnectionTimeout)
|
||||
};
|
||||
|
||||
// Most web services expect that UserAgent is set, so we declare it globally
|
||||
HttpClient.DefaultRequestHeaders.UserAgent.ParseAdd(SharedInfo.AssemblyName + "/" + SharedInfo.Version);
|
||||
}
|
||||
|
||||
public void Dispose() => HttpClient.Dispose();
|
||||
|
||||
internal static void Init() {
|
||||
// Set max connection limit from default of 2 to desired value
|
||||
ServicePointManager.DefaultConnectionLimit = MaxConnections;
|
||||
@@ -94,7 +100,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
byte[] result = null;
|
||||
for (byte i = 0; (i < MaxRetries) && (result == null); i++) {
|
||||
for (byte i = 0; (i < MaxTries) && (result == null); i++) {
|
||||
result = await UrlGetToBytes(request, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -102,7 +108,7 @@ namespace ArchiSteamFarm {
|
||||
return result;
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxRetries));
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxTries));
|
||||
ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request));
|
||||
return null;
|
||||
}
|
||||
@@ -114,7 +120,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
HtmlDocument result = null;
|
||||
for (byte i = 0; (i < MaxRetries) && (result == null); i++) {
|
||||
for (byte i = 0; (i < MaxTries) && (result == null); i++) {
|
||||
result = await UrlGetToHtmlDocument(request, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -122,7 +128,7 @@ namespace ArchiSteamFarm {
|
||||
return result;
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxRetries));
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxTries));
|
||||
ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request));
|
||||
return null;
|
||||
}
|
||||
@@ -134,7 +140,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
JObject result = null;
|
||||
for (byte i = 0; (i < MaxRetries) && (result == null); i++) {
|
||||
for (byte i = 0; (i < MaxTries) && (result == null); i++) {
|
||||
result = await UrlGetToJObject(request, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -142,7 +148,7 @@ namespace ArchiSteamFarm {
|
||||
return result;
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxRetries));
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxTries));
|
||||
ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request));
|
||||
return null;
|
||||
}
|
||||
@@ -150,12 +156,12 @@ namespace ArchiSteamFarm {
|
||||
internal async Task<T> UrlGetToJsonResultRetry<T>(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
ArchiLogger.LogNullError(nameof(request));
|
||||
return default(T);
|
||||
return default;
|
||||
}
|
||||
|
||||
string json = await UrlGetToContentRetry(request, referer).ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(json)) {
|
||||
return default(T);
|
||||
return default;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -167,7 +173,7 @@ namespace ArchiSteamFarm {
|
||||
ArchiLogger.LogGenericDebug(string.Format(Strings.Content, json));
|
||||
}
|
||||
|
||||
return default(T);
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +184,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
XmlDocument result = null;
|
||||
for (byte i = 0; (i < MaxRetries) && (result == null); i++) {
|
||||
for (byte i = 0; (i < MaxTries) && (result == null); i++) {
|
||||
result = await UrlGetToXML(request, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -186,7 +192,7 @@ namespace ArchiSteamFarm {
|
||||
return result;
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxRetries));
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxTries));
|
||||
ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request));
|
||||
return null;
|
||||
}
|
||||
@@ -198,7 +204,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
for (byte i = 0; (i < MaxRetries) && !result; i++) {
|
||||
for (byte i = 0; (i < MaxTries) && !result; i++) {
|
||||
result = await UrlHead(request, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -206,7 +212,7 @@ namespace ArchiSteamFarm {
|
||||
return true;
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxRetries));
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxTries));
|
||||
ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request));
|
||||
return false;
|
||||
}
|
||||
@@ -218,7 +224,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
Uri result = null;
|
||||
for (byte i = 0; (i < MaxRetries) && (result == null); i++) {
|
||||
for (byte i = 0; (i < MaxTries) && (result == null); i++) {
|
||||
result = await UrlHeadToUri(request, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -226,7 +232,7 @@ namespace ArchiSteamFarm {
|
||||
return result;
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxRetries));
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxTries));
|
||||
ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request));
|
||||
return null;
|
||||
}
|
||||
@@ -249,7 +255,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
for (byte i = 0; (i < MaxRetries) && !result; i++) {
|
||||
for (byte i = 0; (i < MaxTries) && !result; i++) {
|
||||
result = await UrlPost(request, data, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -257,7 +263,7 @@ namespace ArchiSteamFarm {
|
||||
return true;
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxRetries));
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxTries));
|
||||
ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request));
|
||||
return false;
|
||||
}
|
||||
@@ -275,12 +281,12 @@ namespace ArchiSteamFarm {
|
||||
internal async Task<T> UrlPostToJsonResultRetry<T>(string request, ICollection<KeyValuePair<string, string>> data = null, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
ArchiLogger.LogNullError(nameof(request));
|
||||
return default(T);
|
||||
return default;
|
||||
}
|
||||
|
||||
string json = await UrlPostToContentRetry(request, data, referer).ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(json)) {
|
||||
return default(T);
|
||||
return default;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -292,7 +298,7 @@ namespace ArchiSteamFarm {
|
||||
ArchiLogger.LogGenericDebug(string.Format(Strings.Content, json));
|
||||
}
|
||||
|
||||
return default(T);
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,7 +339,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
string result = null;
|
||||
for (byte i = 0; (i < MaxRetries) && string.IsNullOrEmpty(result); i++) {
|
||||
for (byte i = 0; (i < MaxTries) && string.IsNullOrEmpty(result); i++) {
|
||||
result = await UrlGetToContent(request, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -341,7 +347,7 @@ namespace ArchiSteamFarm {
|
||||
return result;
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxRetries));
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxTries));
|
||||
ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request));
|
||||
return null;
|
||||
}
|
||||
@@ -380,12 +386,13 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> UrlGetToResponse(string request, string referer = null) {
|
||||
if (!string.IsNullOrEmpty(request)) {
|
||||
return await UrlRequest(new Uri(request), HttpMethod.Get, null, referer).ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
ArchiLogger.LogNullError(nameof(request));
|
||||
return null;
|
||||
}
|
||||
|
||||
ArchiLogger.LogNullError(nameof(request));
|
||||
return null;
|
||||
HttpResponseMessage result = await UrlRequest(new Uri(request), HttpMethod.Get, null, referer).ConfigureAwait(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<XmlDocument> UrlGetToXML(string request, string referer = null) {
|
||||
@@ -464,7 +471,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
string result = null;
|
||||
for (byte i = 0; (i < MaxRetries) && string.IsNullOrEmpty(result); i++) {
|
||||
for (byte i = 0; (i < MaxTries) && string.IsNullOrEmpty(result); i++) {
|
||||
result = await UrlPostToContent(request, data, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -472,7 +479,7 @@ namespace ArchiSteamFarm {
|
||||
return result;
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxRetries));
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxTries));
|
||||
ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request));
|
||||
return null;
|
||||
}
|
||||
@@ -486,7 +493,7 @@ namespace ArchiSteamFarm {
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> UrlRequest(Uri requestUri, HttpMethod httpMethod, ICollection<KeyValuePair<string, string>> data = null, string referer = null, byte maxRedirections = MaxRetries) {
|
||||
private async Task<HttpResponseMessage> UrlRequest(Uri requestUri, HttpMethod httpMethod, ICollection<KeyValuePair<string, string>> data = null, string referer = null, byte maxRedirections = MaxTries) {
|
||||
if ((requestUri == null) || (httpMethod == null)) {
|
||||
ArchiLogger.LogNullError(nameof(requestUri) + " || " + nameof(httpMethod));
|
||||
return null;
|
||||
@@ -532,7 +539,17 @@ namespace ArchiSteamFarm {
|
||||
ushort status = (ushort) responseMessage.StatusCode;
|
||||
if ((status >= 300) && (status <= 399) && (maxRedirections > 0)) {
|
||||
redirectUri = responseMessage.Headers.Location;
|
||||
if (!redirectUri.IsAbsoluteUri) {
|
||||
|
||||
if (redirectUri.IsAbsoluteUri) {
|
||||
switch (redirectUri.Scheme) {
|
||||
case "http":
|
||||
case "https":
|
||||
break;
|
||||
default:
|
||||
// Invalid ones such as "steammobile"
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
redirectUri = new Uri(requestUri.GetLeftPart(UriPartial.Authority) + redirectUri);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"AutoRestart": true,
|
||||
"AutoUpdates": true,
|
||||
"BackgroundGCPeriod": 0,
|
||||
"Blacklist": [],
|
||||
"ConnectionTimeout": 60,
|
||||
"CurrentCulture": null,
|
||||
@@ -8,7 +9,7 @@
|
||||
"FarmingDelay": 15,
|
||||
"GiftsLimiterDelay": 1,
|
||||
"Headless": false,
|
||||
"IdleFarmingPeriod": 3,
|
||||
"IdleFarmingPeriod": 8,
|
||||
"InventoryLimiterDelay": 3,
|
||||
"IPCHost": "127.0.0.1",
|
||||
"IPCPort": 1242,
|
||||
@@ -18,6 +19,6 @@
|
||||
"OptimizationMode": 0,
|
||||
"Statistics": true,
|
||||
"SteamOwnerID": 0,
|
||||
"SteamProtocol": 6,
|
||||
"SteamProtocols": 4,
|
||||
"UpdateChannel": 1
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"AcceptGifts": false,
|
||||
"AutoDiscoveryQueue": false,
|
||||
"CardDropsRestricted": true,
|
||||
"CustomGamePlayedWhileFarming": null,
|
||||
"CustomGamePlayedWhileIdle": null,
|
||||
@@ -9,12 +10,16 @@
|
||||
"FarmOffline": false,
|
||||
"GamesPlayedWhileIdle": [],
|
||||
"HandleOfflineMessages": false,
|
||||
"IdleRefundableGames": true,
|
||||
"IsBotAccount": false,
|
||||
"LootableTypes": [
|
||||
1,
|
||||
3,
|
||||
5
|
||||
],
|
||||
"MatchableTypes": [
|
||||
5
|
||||
],
|
||||
"PasswordFormat": 0,
|
||||
"Paused": false,
|
||||
"RedeemingPreferences": 0,
|
||||
|
||||
File diff suppressed because one or more lines are too long
93
appveyor.yml
93
appveyor.yml
@@ -5,44 +5,81 @@ branches:
|
||||
only:
|
||||
- master
|
||||
skip_branch_with_pr: true
|
||||
image: Visual Studio 2017 Preview
|
||||
image: Visual Studio 2017
|
||||
configuration: Release
|
||||
platform: Any CPU
|
||||
clone_depth: 10
|
||||
environment:
|
||||
DOTNET_CHANNEL: 2.0
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: true
|
||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
||||
before_build:
|
||||
- ps: dotnet restore
|
||||
build:
|
||||
project: ArchiSteamFarm.sln
|
||||
parallel: true
|
||||
verbosity: minimal
|
||||
after_build:
|
||||
RUNTIMES: generic win-x64 linux-x64 linux-arm osx-x64
|
||||
matrix:
|
||||
fast_finish: true
|
||||
install:
|
||||
- ps: >-
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
dotnet --info
|
||||
|
||||
$RUNTIMES = 'generic', 'win-x64', 'linux-x64', 'linux-arm', 'osx-x64'
|
||||
$env:DOTNET_INSTALL_DIR = "$pwd\.dotnetcli"
|
||||
|
||||
|
||||
foreach ($RUNTIME in $RUNTIMES) {
|
||||
if ($RUNTIME -eq 'generic') {
|
||||
dotnet publish -c "$env:CONFIGURATION" -o "out\$RUNTIME"
|
||||
} else {
|
||||
dotnet publish -c "$env:CONFIGURATION" -r "$RUNTIME" -o "out\$RUNTIME"
|
||||
}
|
||||
New-Item '.\scripts\obtain' -ItemType 'directory' -Force
|
||||
|
||||
Add-Content "ArchiSteamFarm\out\$RUNTIME\ArchiSteamFarm.version" "$RUNTIME"
|
||||
Invoke-WebRequest -Uri 'https://raw.githubusercontent.com/dotnet/cli/master/scripts/obtain/dotnet-install.ps1' -OutFile '.\scripts\obtain\dotnet-install.ps1'
|
||||
|
||||
7z a -bd -tzip -mm=Deflate64 -mx=1 "ArchiSteamFarm\out\ASF-$RUNTIME.zip" "$env:APPVEYOR_BUILD_FOLDER\ArchiSteamFarm\out\$RUNTIME\*"
|
||||
Push-AppveyorArtifact "ArchiSteamFarm\out\ASF-$RUNTIME.zip" -FileName "ASF-$RUNTIME.zip" -DeploymentName "ASF-$RUNTIME.zip"
|
||||
.\scripts\obtain\dotnet-install.ps1 -Channel "$env:DOTNET_CHANNEL" -InstallDir "$env:DOTNET_INSTALL_DIR"
|
||||
before_build:
|
||||
- ps: >-
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
dotnet --info
|
||||
|
||||
dotnet restore
|
||||
build_script:
|
||||
- ps: >-
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
dotnet build -c "$env:CONFIGURATION" -o 'out\source' --no-restore /nologo
|
||||
test_script:
|
||||
- ps: >-
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
dotnet test ArchiSteamFarm.Tests -c "$env:CONFIGURATION" -o 'out\source' --no-build --no-restore
|
||||
after_test:
|
||||
- ps: >-
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$PublishBlock = {
|
||||
param($RUNTIME)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
Set-Location -Path "$env:APPVEYOR_BUILD_FOLDER"
|
||||
|
||||
if ($RUNTIME -eq 'generic') {
|
||||
dotnet publish ArchiSteamFarm -c "$env:CONFIGURATION" -o "out\$RUNTIME" --no-restore /nologo
|
||||
} else {
|
||||
dotnet publish ArchiSteamFarm -c "$env:CONFIGURATION" -o "out\$RUNTIME" -r "$RUNTIME" --no-restore /nologo
|
||||
}
|
||||
|
||||
Set-Content -Path "ArchiSteamFarm\out\$RUNTIME\ArchiSteamFarm.version" -Value "$RUNTIME"
|
||||
|
||||
7z a -bd -tzip -mm=Deflate64 -mx=9 -mfb=257 -mpass=3 "ArchiSteamFarm\out\ASF-$RUNTIME.zip" "$env:APPVEYOR_BUILD_FOLDER\ArchiSteamFarm\out\$RUNTIME\*"
|
||||
Push-AppveyorArtifact "ArchiSteamFarm\out\ASF-$RUNTIME.zip" -FileName "ASF-$RUNTIME.zip" -DeploymentName "ASF-$RUNTIME.zip"
|
||||
}
|
||||
|
||||
foreach ($RUNTIME in $env:RUNTIMES.Split([char[]] $null, [System.StringSplitOptions]::RemoveEmptyEntries)) {
|
||||
Start-Job -Name "$RUNTIME" -ScriptBlock $PublishBlock -ArgumentList "$RUNTIME"
|
||||
}
|
||||
|
||||
Get-Job | Receive-Job -AutoRemoveJob -Wait
|
||||
deploy:
|
||||
- provider: GitHub
|
||||
tag: $(appveyor_repo_tag_name)
|
||||
release: ASF V$(appveyor_repo_tag_name)
|
||||
description: '**NOTICE:** Pre-releases are experimental versions that often contain unpatched bugs, work-in-progress features or rewritten implementations. If you don''t consider yourself advanced user, please download **[latest stable release](https://github.com/JustArchi/ArchiSteamFarm/releases/latest)** instead. Pre-release versions are dedicated to users who know how to report bugs, deal with issues and give feedback - no technical support will be given. Check out ASF **[release cycle](https://github.com/JustArchi/ArchiSteamFarm/wiki/Release-cycle)** if you''d like to learn more.\n\n---\n\nThis is automated AppVeyor GitHub deployment, human-readable changelog should be available soon. In the meantime please refer to **[GitHub commits](https://github.com/JustArchi/ArchiSteamFarm/commits/$(appveyor_repo_tag_name))**.\n\n---\n\nASF is available for free. If you''re grateful for what we''re doing, please consider donating. Developing ASF requires massive amount of time and knowledge, especially when it comes to Steam (and its problems). Even 1$ is highly appreciated and shows that you care!\n\n [](https://www.patreon.com/JustArchi) [](https://www.paypal.me/JustArchi/1usd) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=HD2P2P3WGS5Y4) [](https://blockchain.info/payment_request?address=1Archi6M1r5b41Rvn1SY2FfJAzsrEUT7aT) [](https://steamcommunity.com/tradeoffer/new/?partner=46697991&token=0ix2Ruv_)'
|
||||
release: ArchiSteamFarm V$(appveyor_repo_tag_name)
|
||||
description: '### **NOTICE:** Pre-releases are experimental versions that often contain unpatched bugs, work-in-progress features or rewritten implementations. If you don''t consider yourself advanced user, please download **[latest stable release](https://github.com/JustArchi/ArchiSteamFarm/releases/latest)** instead. Pre-release versions are dedicated to users who know how to report bugs, deal with issues and give feedback - no technical support will be given. Check out ASF **[release cycle](https://github.com/JustArchi/ArchiSteamFarm/wiki/Release-cycle)** if you''d like to learn more.\n\n---\n\nThis is automated AppVeyor GitHub deployment, human-readable changelog should be available soon. In the meantime please refer to **[GitHub commits](https://github.com/JustArchi/ArchiSteamFarm/commits/$(appveyor_repo_tag_name))**.\n\n---\n\nASF is available for free. If you''re grateful for what we''re doing, please consider donating. Developing ASF requires massive amount of time and knowledge, especially when it comes to Steam (and its problems). Even 1$ is highly appreciated and shows that you care!\n\n [](https://www.patreon.com/JustArchi) [](https://www.paypal.me/JustArchi/1usd) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=HD2P2P3WGS5Y4) [](https://blockchain.info/payment_request?address=1Archi6M1r5b41Rvn1SY2FfJAzsrEUT7aT) [](https://steamcommunity.com/tradeoffer/new/?partner=46697991&token=0ix2Ruv_)'
|
||||
auth_token:
|
||||
secure: QC5gIDMvSpd43EG6qW8d1E3ZHiVU4aR7pbKQonXstjj/JtAABf5S1IbtoY4OsnOR
|
||||
artifact: /.*/
|
||||
@@ -52,4 +89,18 @@ deploy:
|
||||
on:
|
||||
branch: master
|
||||
configuration: Release
|
||||
appveyor_repo_tag: true
|
||||
appveyor_repo_tag: true
|
||||
notifications:
|
||||
- provider: Webhook
|
||||
url:
|
||||
secure: i/og7KzkpbcWcKoUubrLH+KB6bkfqA55FHUlGxLepLmgZNQeNMiMoAFICOFY795fFrFfUNUKqwk7ApXE6HUyWMoiijLj7G/JBLTPkBiTCu8fZMTMqwQm6FiHB3+/0h0C+ukcrBEqnXYSQUh6znpKqJSTrIfXUQ7ftNuC966kBAw=
|
||||
method: POST
|
||||
body: >-
|
||||
{
|
||||
"avatar_url": "https://www.appveyor.com/assets/img/appveyor-logo-256.png",
|
||||
"username": "AppVeyor",
|
||||
"content": "[{{projectName}}:{{branch}}] {{commitMessage}} - {{committerName}} ({{commitId}}) | {{buildUrl}}/artifacts | **{{status}}**"
|
||||
}
|
||||
on_build_success: true
|
||||
on_build_failure: true
|
||||
on_build_status_changed: false
|
||||
25
cc.sh
25
cc.sh
@@ -2,10 +2,11 @@
|
||||
set -eu
|
||||
|
||||
PROJECT="ArchiSteamFarm"
|
||||
OUT="out"
|
||||
MSBUILD_ARGS=("/nologo")
|
||||
OUT="out/source"
|
||||
|
||||
SOLUTION="${PROJECT}.sln"
|
||||
CONFIGURATION="Release"
|
||||
|
||||
BUILD="Release"
|
||||
CLEAN=0
|
||||
|
||||
PRINT_USAGE() {
|
||||
@@ -15,8 +16,8 @@ PRINT_USAGE() {
|
||||
|
||||
for ARG in "$@"; do
|
||||
case "$ARG" in
|
||||
release|Release) BUILD="Release" ;;
|
||||
debug|Debug) BUILD="Debug" ;;
|
||||
release|Release) CONFIGURATION="Release" ;;
|
||||
debug|Debug) CONFIGURATION="Debug" ;;
|
||||
--clean) CLEAN=1 ;;
|
||||
*) PRINT_USAGE
|
||||
esac
|
||||
@@ -27,24 +28,28 @@ if ! hash dotnet &>/dev/null; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dotnet --info
|
||||
|
||||
cd "$(dirname "$(readlink -f "$0")")"
|
||||
|
||||
if [[ -d ".git" ]] && hash git &>/dev/null; then
|
||||
git pull || true
|
||||
fi
|
||||
|
||||
if [[ ! -f "${PROJECT}.sln" ]]; then
|
||||
echo "ERROR: ${PROJECT}.sln could not be found!"
|
||||
if [[ ! -f "$SOLUTION" ]]; then
|
||||
echo "ERROR: $SOLUTION could not be found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$CLEAN" -eq 1 ]]; then
|
||||
dotnet clean -c "$BUILD" -o "$OUT"
|
||||
rm -rf "$OUT"
|
||||
dotnet clean -c "$CONFIGURATION" -o "$OUT"
|
||||
rm -rf "ArchiSteamFarm/${OUT}" "ArchiSteamFarm.Tests/${OUT}"
|
||||
fi
|
||||
|
||||
dotnet restore
|
||||
dotnet build -c "$BUILD" -o "$OUT" "${MSBUILD_ARGS[@]}"
|
||||
|
||||
dotnet build -c "$CONFIGURATION" -o "$OUT" --no-restore /nologo
|
||||
dotnet test ArchiSteamFarm.Tests -c "$CONFIGURATION" -o "$OUT" --no-build --no-restore
|
||||
|
||||
echo
|
||||
echo "Compilation finished successfully! :)"
|
||||
|
||||
7
run.sh
7
run.sh
@@ -2,7 +2,8 @@
|
||||
set -eu
|
||||
|
||||
PROJECT="ArchiSteamFarm"
|
||||
OUT="out"
|
||||
OUT="out/source"
|
||||
|
||||
BINARY="${PROJECT}/${OUT}/${PROJECT}.dll"
|
||||
|
||||
ASF_ARGS=("")
|
||||
@@ -28,6 +29,8 @@ if ! hash dotnet &>/dev/null; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dotnet --info
|
||||
|
||||
cd "$(dirname "$(readlink -f "$0")")"
|
||||
|
||||
if [[ ! -f "$BINARY" ]]; then
|
||||
@@ -37,7 +40,7 @@ fi
|
||||
|
||||
if [[ "$UNTIL_CLEAN_EXIT" -eq 0 ]]; then
|
||||
dotnet exec "$BINARY" "${ASF_ARGS[@]}"
|
||||
exit $?
|
||||
exit $? # In this case $? can only be 0 because otherwise set -e terminates the script
|
||||
fi
|
||||
|
||||
while [[ -f "$BINARY" ]]; do
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -4,14 +4,14 @@ NetHook2
|
||||
This tool is used for reverse-engineering of Steam client. It's capable of hooking and recording network traffic sent/received by the client. If you're not trying to implement missing SK2 functionality in ASF, then please do not proceed.
|
||||
|
||||
1. Launch Steam client
|
||||
2. Execute ```hook.bat```
|
||||
2. Execute `hook.cmd`
|
||||
3. Reproduce the functionality you're trying to add
|
||||
4. Execute ```unhook.bat```
|
||||
5. Use ```NetHookAnalyzer2.exe``` for analyzing recorded log (which can be found in your Steam directory)
|
||||
4. Execute `unhook.cmd`
|
||||
5. Use `NetHookAnalyzer2.exe` for analyzing recorded log (which can be found in your Steam directory)
|
||||
|
||||
- Source of the ```NetHook2.dll``` can be found **[here](https://github.com/SteamRE/SteamKit/tree/master/Resources/NetHook2)**
|
||||
- Source of the ```NetHookAnalyzer2.exe``` can be found **[here](https://github.com/SteamRE/SteamKit/tree/master/Resources/NetHookAnalyzer2)**
|
||||
- Source of the `NetHook2.dll` can be found **[here](https://github.com/SteamRE/SteamKit/tree/master/Resources/NetHook2)**
|
||||
- Source of the `NetHookAnalyzer2.exe` can be found **[here](https://github.com/SteamRE/SteamKit/tree/master/Resources/NetHookAnalyzer2)**
|
||||
|
||||
===================
|
||||
|
||||
There is absolutely no guarantee that this will even work for you, not to mention the consequences from hooking the external DLL into steam client. You're on your own.
|
||||
There is absolutely no guarantee that this will even work for you, not to mention the consequences from hooking the external DLL into steam client. You're on your own. This build is for me so I don't need to compile it from scratch every time - I strongly recommend against using it. You have SK2 sources for a reason.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -15,9 +15,9 @@ This tool is being used by ASF developers for synchronization of strings/transla
|
||||
|
||||
## Before you begin
|
||||
|
||||
- Make sure that your ```crowdin_identity.yaml``` file exists - this is the file with login credentials that is not being committed to GitHub. If it doesn't exist yet (e.g. because you've just cloned the repo), create it from ```crowdin_identity_example.yaml``` and fill ```api_key``` that can be found **[here](http://l10n.asf.justarchi.net/project/archisteamfarm/settings#api)**.
|
||||
- Make sure that your `crowdin_identity.yaml` file exists - this is the file with login credentials that is not being committed to GitHub. If it doesn't exist yet (e.g. because you've just cloned the repo), create it from `crowdin_identity_example.yaml` and fill `api_key` that can be found **[here](http://l10n.asf.justarchi.net/project/archisteamfarm/settings#api)**.
|
||||
|
||||
- Ensure that ```crowdin``` command is recognized by your OS.
|
||||
- Ensure that `crowdin` command is recognized by your OS.
|
||||
|
||||
---
|
||||
|
||||
@@ -25,14 +25,16 @@ This tool is being used by ASF developers for synchronization of strings/transla
|
||||
|
||||
- Install **[Java JDK](http://www.oracle.com/technetwork/java/javase/downloads/index.html)**.
|
||||
- **[Set JAVA_HOME properly](https://confluence.atlassian.com/doc/setting-the-java_home-variable-in-windows-8895.html)**.
|
||||
- Launch ```setup_crowdin.bat``` as administrator.
|
||||
- Open new ```cmd``` prompt and verify that ```crowdin help``` indeed works.
|
||||
- Launch `setup_crowdin.bat` as administrator.
|
||||
- Open new `cmd` prompt and verify that `crowdin help` indeed works.
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
- ```archi_download.bat``` for downloading translations from Crowdin (typically last commit before release).
|
||||
- `archi_upload.bat` for pushing strings to Crowdin (when any `*Strings.resx` file gets modified).
|
||||
|
||||
- ```archi_upload.bat``` for pushing strings to Crowdin (when any ```*Strings.resx``` file gets modified).
|
||||
- `archi_download.bat` for downloading translations from Crowdin (typically last commit before release).
|
||||
|
||||
- `archi_sync.bat` for upload + download (tree sync, e.g. when modifying/removing original strings).
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@echo off
|
||||
pushd %~dp0
|
||||
cd ..\\..
|
||||
call crowdin -b master --identity tools\\crowdin-cli\\crowdin_identity.yaml download
|
||||
pause
|
||||
|
||||
6
tools/crowdin-cli/archi_sync.bat
Normal file
6
tools/crowdin-cli/archi_sync.bat
Normal file
@@ -0,0 +1,6 @@
|
||||
@echo off
|
||||
pushd %~dp0
|
||||
cd ..\\..
|
||||
call crowdin -b master --identity tools\\crowdin-cli\\crowdin_identity.yaml upload sources
|
||||
call crowdin -b master --identity tools\\crowdin-cli\\crowdin_identity.yaml download
|
||||
pause
|
||||
@@ -1,4 +1,5 @@
|
||||
@echo off
|
||||
pushd %~dp0
|
||||
cd ..\\..
|
||||
call crowdin -b master --identity tools\\crowdin-cli\\crowdin_identity.yaml upload sources
|
||||
pause
|
||||
|
||||
Binary file not shown.
@@ -2,6 +2,7 @@
|
||||
SETLOCAL
|
||||
SET TEMPFILE=%TEMP%\tmpfile
|
||||
|
||||
pushd %~dp0
|
||||
setx /M CROWDIN_HOME "%cd%"
|
||||
setx /M PATH "%PATH%;%cd%"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user