Created
September 3, 2019 01:37
-
-
Save Pro100AlexHell/b97d706516fcc54badf08b3773ab309c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.AlexHell; | |
import java.util.ArrayList; | |
import java.util.Collections; | |
/** | |
* Ресолвер поиска Пути Коня с помощью BFS | |
* @implNote не потокобезопасный, с оптимизациям GC и CPU-cache | |
*/ | |
public final class KnightPathResolver | |
{ | |
/** | |
* Размеры поля | |
*/ | |
private static final int MapWidth = 8; | |
private static final int MapHeight = 8; | |
/** | |
* Размер очереди (в реальности меньше чем размер поля, но сложно подобрать, гарантируем не-переполнение) | |
*/ | |
private static final int PointQueueMaxCount = MapWidth * MapHeight; | |
/** | |
* Типы задаваемые в карте | |
* - отрицательные (-1 и -2) - можно посещать | |
* - положительные (или 0) - посещенные, с указанием Parent индекса, откуда пришли | |
*/ | |
private static final int TypeNotFilled = -1;// Не посещено | |
private static final int TypeTarget = -2; // Целевая точка | |
private static final int TypeStart = Integer.MAX_VALUE; // Начальная точка (чтобы ее нельзя было посетить еще раз) | |
/** | |
* Карта типов сущностей в одномерном массиве | |
* (по индексации - см. реализацию) | |
* (по типам - см выше) | |
* (Y == 0 внизу, X == 0 слева) | |
*/ | |
private static final int[] Entries = new int[MapWidth * MapHeight]; | |
/** | |
* Очередь BFS: индексов точек | |
*/ | |
private static final PointQueue PointQueue = new PointQueue(PointQueueMaxCount); | |
/** | |
* Поиск пути коня | |
* @param knightPathInputParams Входные данные (начало, конец) | |
* @return Путь (null - либо исходная равна конечной, либо пути не существует.. todo: могло бы быть если бы на карте были препятствия) | |
*/ | |
public static KnightPathOutputParams Resolve(KnightPathInputParams knightPathInputParams) | |
{ | |
if (knightPathInputParams.From.X == knightPathInputParams.To.X && | |
knightPathInputParams.From.Y == knightPathInputParams.To.Y) | |
{ | |
return null; | |
} | |
InitMap(knightPathInputParams); | |
PointQueue.Reset(); | |
AddPointToQueue(knightPathInputParams.From.X, knightPathInputParams.From.Y); | |
boolean isResultFound = MainBfs(); | |
if (isResultFound) | |
{ | |
return FillResult(knightPathInputParams); | |
} | |
else | |
{ | |
return null; | |
} | |
} | |
private static void InitMap(KnightPathInputParams knightPathInputParams) | |
{ | |
for (int i = 0; i < MapWidth * MapHeight; i++) | |
{ | |
Entries[i] = TypeNotFilled; | |
} | |
SetEntry(knightPathInputParams.From.X, knightPathInputParams.From.Y, TypeStart); | |
SetEntry(knightPathInputParams.To.X, knightPathInputParams.To.Y, TypeTarget); | |
} | |
private static void SetEntry(int x, int y, int value) | |
{ | |
Entries[CombinePoint(x, y)] = value; | |
} | |
private static void AddPointToQueue(int x, int y) | |
{ | |
PointQueue.AddLast(CombinePoint(x, y)); | |
} | |
/** | |
* Основная работа BFS | |
* @return Признак: Найден результат, нужно построить путь | |
*/ | |
private static boolean MainBfs() | |
{ | |
boolean isResultFound = false; | |
while (PointQueue.HasItems()) | |
{ | |
int startPointIndex = PointQueue.PopFirst(); | |
int startPointX = startPointIndex % MapWidth; | |
int startPointY = startPointIndex / MapWidth; | |
for (int dir = 0; dir <= KnightDirIndexMax; dir++) | |
{ | |
int newPointIndex = GeneratePointShiftedByKnightDir(startPointX, startPointY, dir); | |
if (newPointIndex != -1) | |
{ | |
int newEntry = Entries[newPointIndex]; | |
if (newEntry == TypeTarget) | |
{ | |
Entries[newPointIndex] = startPointIndex; | |
isResultFound = true; | |
break; | |
} | |
else if (newEntry == TypeNotFilled) | |
{ | |
Entries[newPointIndex] = startPointIndex; | |
PointQueue.AddLast(newPointIndex); | |
} | |
// else - уже посещали | |
} | |
} | |
} | |
return isResultFound; | |
} | |
/** | |
* Всего 8 направлений хода коня (0-7) | |
*/ | |
private static final int KnightDirIndexMax = 7; | |
/** | |
* Генерация новой точки с помощью хода коня | |
* @param startPointX Координаты начальной точки | |
* @param startPointY | |
* @param dir Индекс направления (0-7) | |
* @return Индекс новой точки (с проверкой выхода за границы карты, -1 если вышли за границы) | |
*/ | |
private static int GeneratePointShiftedByKnightDir(int startPointX, int startPointY, int dir) | |
{ | |
switch (dir) | |
{ | |
/* | |
* +++++ | |
* +++++ | |
* ++K++ | |
* ++++0 | |
* +++++ | |
* | |
* здесь и далее обозначено: | |
* K - исходная позиция | |
* 0 - целевая позиция | |
*/ | |
case 0: | |
{ | |
int dx = 2; | |
int dy = -1; | |
if (startPointX + dx >= MapWidth) return -1; | |
if (startPointY + dy < 0) return -1; | |
return CombinePointWithDxDy(startPointX, startPointY, dx, dy); | |
} | |
/* | |
* +++++ | |
* ++++0 | |
* ++K++ | |
* +++++ | |
* +++++ | |
*/ | |
case 1: | |
{ | |
int dx = 2; | |
int dy = 1; | |
if (startPointX + dx >= MapWidth) return -1; | |
if (startPointY + dy >= MapHeight) return -1; | |
return CombinePointWithDxDy(startPointX, startPointY, dx, dy); | |
} | |
/* | |
* +++++ | |
* 0++++ | |
* ++K++ | |
* +++++ | |
* +++++ | |
*/ | |
case 2: | |
{ | |
int dx = -2; | |
int dy = 1; | |
if (startPointX + dx < 0) return -1; | |
if (startPointY + dy >= MapHeight) return -1; | |
return CombinePointWithDxDy(startPointX, startPointY, dx, dy); | |
} | |
/* | |
* +++++ | |
* +++++ | |
* ++K++ | |
* 0++++ | |
* +++++ | |
*/ | |
case 3: | |
{ | |
int dx = -2; | |
int dy = -1; | |
if (startPointX + dx < 0) return -1; | |
if (startPointY + dy < 0) return -1; | |
return CombinePointWithDxDy(startPointX, startPointY, dx, dy); | |
} | |
/* | |
* +++++ | |
* +++++ | |
* ++K++ | |
* +++++ | |
* +++0+ | |
*/ | |
case 4: | |
{ | |
int dx = 1; | |
int dy = -2; | |
if (startPointX + dx >= MapWidth) return -1; | |
if (startPointY + dy < 0) return -1; | |
return CombinePointWithDxDy(startPointX, startPointY, dx, dy); | |
} | |
/* | |
* +++0+ | |
* +++++ | |
* ++K++ | |
* +++++ | |
* +++++ | |
*/ | |
case 5: | |
{ | |
int dx = 1; | |
int dy = 2; | |
if (startPointX + dx >= MapWidth) return -1; | |
if (startPointY + dy >= MapHeight) return -1; | |
return CombinePointWithDxDy(startPointX, startPointY, dx, dy); | |
} | |
/* | |
* +0+++ | |
* +++++ | |
* ++K++ | |
* +++++ | |
* +++++ | |
*/ | |
case 6: | |
{ | |
int dx = -1; | |
int dy = 2; | |
if (startPointX + dx < 0) return -1; | |
if (startPointY + dy >= MapHeight) return -1; | |
return CombinePointWithDxDy(startPointX, startPointY, dx, dy); | |
} | |
/* | |
* +++++ | |
* +++++ | |
* ++K++ | |
* +++++ | |
* +0+++ | |
*/ | |
default: | |
{ | |
int dx = -1; | |
int dy = -2; | |
if (startPointX + dx < 0) return -1; | |
if (startPointY + dy < 0) return -1; | |
return CombinePointWithDxDy(startPointX, startPointY, dx, dy); | |
} | |
} | |
} | |
private static int CombinePointWithDxDy(int startPointX, int startPointY, int dx, int dy) | |
{ | |
int newX = startPointX + dx; | |
int newY = startPointY + dy; | |
return CombinePoint(newX, newY); | |
} | |
private static int CombinePoint(int x, int y) | |
{ | |
return y * MapWidth + x; | |
} | |
/** | |
* Заполнение результата | |
*/ | |
private static KnightPathOutputParams FillResult(KnightPathInputParams knightPathInputParams) | |
{ | |
KnightPathOutputParams knightPathOutputParams = new KnightPathOutputParams(); | |
knightPathOutputParams.Points = new ArrayList<>(16); /** @todo пока без пула, но при желании можно */ | |
knightPathOutputParams.Points.add(knightPathInputParams.To); | |
// т.к. уже найден каждый Parent - строим от конечной точки к начальной | |
int tempIndex = CombinePoint(knightPathInputParams.To.X, knightPathInputParams.To.Y); | |
while (true) | |
{ | |
int parent = Entries[tempIndex]; | |
if (parent == TypeStart) | |
{ | |
break; | |
} | |
int parentX = parent % MapWidth; | |
int parentY = parent / MapWidth; | |
knightPathOutputParams.Points.add(new Point(parentX, parentY)); | |
tempIndex = parent; | |
} | |
// инвертируем чтобы получилось от начальной точки к конечной | |
Collections.reverse(knightPathOutputParams.Points); | |
return knightPathOutputParams; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//#define DEBUG_TIMING // todo debug only | |
//#define DEBUG_LOADER_LOGS // todo debug only | |
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.IO; | |
using Assets.LoadableFiles.FileNameResolvers; | |
using Assets.LoadableFiles.SlicedArrayByte; | |
using Assets.LoadableFiles.WebOnly.WebLoaderToRamFromCache; | |
using Assets.SVG_Importer.Utils; | |
using UnityEngine; | |
namespace Assets.LoadableFiles | |
{ | |
/// <summary> | |
/// Полноценный загрузчик файлов (с ресолвингом имен, со сложным кешем), | |
/// знающий о способах загрузки на разных платформах | |
/// - Абстрактная базовая форма | |
/// </summary> | |
/// <remarks> | |
/// Применим для загрузки большого кол-ва файлов с произвольным размером, | |
/// плюсы для WebGL: | |
/// * адекватное потребление памяти - переиспользуемые буферы в нашем пространстве | |
/// * (когда доступен IndexedDB) хорошая управляемость кешем | |
/// (например: без опроса изменений на сервере при наличии файла в IndexedDB, в отличие от UnityWebRequest, | |
/// хотя заголовки кеширования влияют на браузерный кеш - отдельная тема ревалидации) | |
/// </remarks> | |
/// <typeparam name="TInput">Подкласс входной информации о файле</typeparam> | |
/// <typeparam name="TResult">Подкласс результата загрузки контента файла</typeparam> | |
/// <typeparam name="TConversionResult">Подкласс результата конверсии контента файла</typeparam> | |
/// <typeparam name="TConversionParam">Тип параметра при конверсии</typeparam> | |
public abstract class PlatformAwareFileLoaderComplex<TInput, TResult, TConversionResult, TConversionParam> | |
where TInput : LoadableFileWrapped | |
where TResult : LoadedFile<TConversionResult, TConversionParam> | |
{ | |
public long DebugTimeLoadBatch; | |
public long DebugTimeLoadSingle; | |
public long DebugTimeLoadSingleLoaderToRamFromCache; | |
/// <summary> | |
/// Сброс всех выданных SlicedArray | |
/// </summary> | |
public void ResetAllSlicedArrays() | |
{ | |
PlatformAwareFileLoaderShared.BuilderOfSlicedArrayByte.Reset(); | |
} | |
/// <summary> | |
/// Загрузка пакета файлов; для вызова в yield | |
/// </summary> | |
/// <param name="listLoadableFile">Список: информация о загружаемом файле</param> | |
/// <param name="listResult">Список для сохранения результатов (параллельно входному списку)</param> | |
public IEnumerator LoadBatch(List<TInput> listLoadableFile, List<TResult> listResult) | |
{ | |
#if DEBUG_TIMING | |
long timeStart = DateTime.Now.Ticks; | |
#endif | |
if (listLoadableFile.Contains(null)) | |
{ | |
throw new Exception("PlatformAwareFileLoaderComplex::LoadBatch: listLoadableFile.Contains(null)"); | |
} | |
// todo NOTE: это отладочное, в релизе из ресурсов не будет, но не удаляем | |
if (PlatformAwareFileLoaderShared.IsLoadFromResources) | |
{ | |
yield return LoadBatchFromResources(listLoadableFile, listResult); | |
} | |
else | |
{ | |
#if UNITY_WEBGL | |
yield return LoadBatchFromWebCache(listLoadableFile, listResult); | |
#elif UNITY_ANDROID || UNITY_IOS | |
yield return LoadBatchFromLocalMobilePack(listLoadableFile, listResult); | |
#else | |
yield return LoadBatchFromLocal(listLoadableFile, listResult); | |
#endif | |
} | |
#if DEBUG_TIMING | |
DebugTimeLoadBatch += (long)(new TimeSpan(DateTime.Now.Ticks - timeStart).TotalMilliseconds); | |
#endif | |
} | |
/// <summary> | |
/// Загрузка пакета файлов из Resources; для вызова в yield | |
/// </summary> | |
/// <remarks>Имя файла без задания расширения, но фактически в Resources находятся с расширением ".bytes"</remarks> | |
/// <param name="listLoadableFile">Список: информация о загружаемом файле</param> | |
/// <param name="listResult">Список для сохранения результатов (параллельно входному списку)</param> | |
private IEnumerator LoadBatchFromResources(List<TInput> listLoadableFile, List<TResult> listResult) | |
{ | |
#if DEBUG_LOADER_LOGS | |
Debug.Log("LoadBatchFromResources"); | |
#endif | |
for (int i = 0; i < listLoadableFile.Count; i++) | |
{ | |
listResult.Add(LoadSingleFromResourcesAction(listLoadableFile[i])); | |
} | |
// (ждем конца фрейма, для пачки замедление не заметно, но без yield синтаксически не пройдет функция) | |
yield return null; | |
} | |
/// <summary> | |
/// Загрузка пакета файлов из кеша в WebGL; для вызова в yield | |
/// </summary> | |
/// <param name="listLoadableFile">Список: информация о загружаемом файле (размер не ограничен, | |
/// но должен быть адекватным из-за лимитов памяти на суммарные загруженные файлы и другие лимиты)</param> | |
/// <param name="listResult">Список для сохранения результатов (параллельно входному списку)</param> | |
private IEnumerator LoadBatchFromWebCache(List<TInput> listLoadableFile, List<TResult> listResult) | |
{ | |
#if DEBUG_LOADER_LOGS | |
Debug.Log("LoadBatchFromWebCache"); | |
#endif | |
List<LoaderToRamFromCacheBatch.LoadRequest> requestList = new List<LoaderToRamFromCacheBatch.LoadRequest>(); | |
for (int i = 0; i < listLoadableFile.Count; i++) | |
{ | |
LoadableFile loadableFile = listLoadableFile[i].LoadableFile; | |
string url = PlatformAwareFileLoaderShared.FileNameResolverForWeb.ResolveShortName( | |
loadableFile.ShortName, loadableFile.FileType); | |
// сразу создаем Handle массива байт, заполняем список результатов с использованием этого Handle, | |
// впоследствии будет заполнение реальных байт внутри массива | |
HandleOfSlicedArrayByte handleForResult = | |
PlatformAwareFileLoaderShared.BuilderOfSlicedArrayByte.BuildArray( | |
loadableFile.UncompressedSize); | |
// | |
listResult.Add(CreateResult(url, handleForResult)); | |
requestList.Add(new LoaderToRamFromCacheBatch.LoadRequest(url, handleForResult)); | |
} | |
yield return LoaderToRamFromCacheBatch.Instance.Load(requestList); | |
} | |
/// <summary> | |
/// Загрузка пакета файлов из пакета для мобильных; для вызова в yield | |
/// </summary> | |
/// <param name="listLoadableFile">Список: информация о загружаемом файле</param> | |
/// <param name="listResult">Список для сохранения результатов (параллельно входному списку)</param> | |
private IEnumerator LoadBatchFromLocalMobilePack(List<TInput> listLoadableFile, List<TResult> listResult) | |
{ | |
#if DEBUG_LOADER_LOGS | |
Debug.Log("LoadBatchFromLocalMobilePack"); | |
#endif | |
for (int i = 0; i < listLoadableFile.Count; i++) | |
{ | |
listResult.Add(LoadSingleFromLocalMobilePackAction(listLoadableFile[i])); | |
} | |
// (ждем конца фрейма, для пачки замедление не заметно, но без yield синтаксически не пройдет функция) | |
yield return null; | |
} | |
/// <summary> | |
/// Загрузка пакета файлов из локального каталога; для вызова в yield | |
/// </summary> | |
/// <param name="listLoadableFile">Список: информация о загружаемом файле</param> | |
/// <param name="listResult">Список для сохранения результатов (параллельно входному списку)</param> | |
private IEnumerator LoadBatchFromLocal(List<TInput> listLoadableFile, List<TResult> listResult) | |
{ | |
#if DEBUG_LOADER_LOGS | |
Debug.Log("LoadBatchFromLocal"); | |
#endif | |
for (int i = 0; i < listLoadableFile.Count; i++) | |
{ | |
listResult.Add(LoadSingleFromLocalAction(listLoadableFile[i])); | |
} | |
// (ждем конца фрейма, для пачки замедление не заметно, но без yield синтаксически не пройдет функция) | |
yield return null; | |
} | |
/// <summary> | |
/// Загрузка файла; для вызова в yield; строго для одиночной загрузки | |
/// </summary> | |
/// <param name="loadableFile">Информация о загружаемом файле</param> | |
/// <param name="result">Для сохранения результата</param> | |
public IEnumerator LoadSingle(TInput loadableFile, CoroutineResult<TResult> result) | |
{ | |
#if DEBUG_TIMING | |
long timeStart2 = DateTime.Now.Ticks; | |
#endif | |
if (loadableFile == null) | |
{ | |
throw new Exception("PlatformAwareFileLoaderComplex::LoadSingle: loadableFile == null"); | |
} | |
// todo NOTE: это отладочное, в релизе из ресурсов не будет, но не удаляем | |
if (PlatformAwareFileLoaderShared.IsLoadFromResources) | |
{ | |
yield return LoadSingleFromResources(loadableFile, result); | |
} | |
else | |
{ | |
#if UNITY_WEBGL | |
yield return LoadSingleFromWebCache(loadableFile, result); | |
#elif UNITY_ANDROID || UNITY_IOS | |
yield return LoadSingleFromLocalMobilePack(loadableFile, result); | |
#else | |
yield return LoadSingleFromLocal(loadableFile, result); | |
#endif | |
} | |
#if DEBUG_TIMING | |
DebugTimeLoadSingle += (long)(new TimeSpan(DateTime.Now.Ticks - timeStart2).TotalMilliseconds); | |
#endif | |
} | |
/// <summary> | |
/// Загрузка файла из Resources; для вызова в yield; строго для одиночной загрузки | |
/// </summary> | |
/// <remarks>Имя файла без задания расширения, но фактически в Resources находятся с расширением ".bytes"</remarks> | |
/// <param name="loadableFile">Информация о загружаемом файле</param> | |
/// <param name="result">Для сохранения результата</param> | |
private IEnumerator LoadSingleFromResources(TInput loadableFile, CoroutineResult<TResult> result) | |
{ | |
#if DEBUG_LOADER_LOGS | |
Debug.Log("LoadSingleFromResources"); | |
#endif | |
result.Value = LoadSingleFromResourcesAction(loadableFile); | |
// (ждем конца фрейма, получая замедление, но без yield синтаксически не пройдет функция) | |
yield return null; | |
} | |
/// <summary> | |
/// Реальная загрузка файла из Resources; строго для одиночной загрузки | |
/// </summary> | |
/// <remarks>Имя файла без задания расширения, но фактически в Resources находятся с расширением ".bytes"</remarks> | |
/// <param name="loadableFile">Информация о загружаемом файле</param> | |
/// <returns>Результат</returns> | |
private TResult LoadSingleFromResourcesAction(TInput loadableFile) | |
{ | |
string fileName = loadableFile.LoadableFile.ShortName; // (без ресолвинга имени т.к. все в плоском каталоге Resources) | |
// (хоть тут и выделяется byte[] лишний - по-другому не сделать, а интерфейс далее общий под Handle) | |
byte[] bytes; | |
try | |
{ | |
TextAsset binAsset = Resources.Load(fileName) as TextAsset; | |
bytes = binAsset.bytes; | |
} | |
catch (Exception) | |
{ | |
throw new Exception("PlatformAwareFileLoaderComplex::LoadSingleFromResourcesAction: Not found FileName = '" + fileName + "'"); | |
} | |
// (UncompressedSize не задан в этом случае, поэтому под размер считанного массива) | |
HandleOfSlicedArrayByte handleForResult = | |
PlatformAwareFileLoaderShared.BuilderOfSlicedArrayByte.BuildArray(bytes.Length); | |
Array.Copy(bytes, 0, | |
handleForResult.TotalBlock, handleForResult.StartIndex, bytes.Length | |
); | |
return CreateResult(fileName, handleForResult); | |
} | |
/// <summary> | |
/// Загрузка файла из кеша в WebGL; для вызова в yield; строго для одиночной загрузки | |
/// </summary> | |
/// <param name="loadableFile">Информация о загружаемом файле</param> | |
/// <param name="result">Для сохранения результата</param> | |
private IEnumerator LoadSingleFromWebCache(TInput loadableFile, CoroutineResult<TResult> result) | |
{ | |
#if DEBUG_LOADER_LOGS | |
Debug.Log("LoadSingleFromWebCache"); | |
#endif | |
#if DEBUG_TIMING | |
long timeStart1 = DateTime.Now.Ticks; | |
#endif | |
string url = PlatformAwareFileLoaderShared.FileNameResolverForWeb.ResolveShortName( | |
loadableFile.LoadableFile.ShortName, loadableFile.LoadableFile.FileType); | |
HandleOfSlicedArrayByte handleForResult = | |
PlatformAwareFileLoaderShared.BuilderOfSlicedArrayByte.BuildArray( | |
loadableFile.LoadableFile.UncompressedSize); | |
LoaderToRamFromCache loader = PlatformAwareFileLoaderShared.SingleLoaderToRamFromCache; | |
loader.StartLoadFile(url, handleForResult); | |
bool isComplete; | |
while (true) | |
{ | |
LoaderToRamFromCacheUpdateResult updateResult = loader.Update(); | |
if (updateResult == LoaderToRamFromCacheUpdateResult.KeepWaiting) | |
{ | |
yield return null; | |
} | |
else | |
{ | |
isComplete = (updateResult == LoaderToRamFromCacheUpdateResult.Complete); | |
break; | |
} | |
} | |
if (isComplete) | |
{ | |
// debug only | |
//Debug.Log("Load to RAM complete, length = " + handleForResult.Length + ", url = " + url); | |
} | |
else | |
{ | |
throw new Exception("PlatformAwareFileLoaderComplex::LoadSingleFromWebCache: Load to RAM ERROR, url = '" + url + "'"); | |
} | |
result.Value = CreateResult(url, handleForResult); | |
#if DEBUG_TIMING | |
DebugTimeLoadSingleLoaderToRamFromCache += (long)(new TimeSpan(DateTime.Now.Ticks - timeStart1).TotalMilliseconds); | |
#endif | |
} | |
/// <summary> | |
/// Загрузка файла из локального каталога; для вызова в yield; строго для одиночной загрузки | |
/// </summary> | |
/// <param name="loadableFile">Информация о загружаемом файле</param> | |
/// <param name="result">Для сохранения результата</param> | |
private IEnumerator LoadSingleFromLocal(TInput loadableFile, CoroutineResult<TResult> result) | |
{ | |
#if DEBUG_LOADER_LOGS | |
Debug.Log("LoadSingleFromLocal"); | |
#endif | |
result.Value = LoadSingleFromLocalAction(loadableFile); | |
// (ждем конца фрейма, получая замедление, но без yield синтаксически не пройдет функция) | |
yield return null; | |
} | |
/// <summary> | |
/// Реальная загрузка файла из локального каталога; строго для одиночной загрузки | |
/// </summary> | |
/// <param name="loadableFile">Информация о загружаемом файле</param> | |
/// <returns>Результат</returns> | |
private TResult LoadSingleFromLocalAction(TInput loadableFile) | |
{ | |
ILoadableFileNameResolver fileNameResolver = PlatformAwareFileLoaderShared.FileNameResolverForWindows; | |
string fileName = fileNameResolver.ResolveShortName(loadableFile.LoadableFile.ShortName, loadableFile.LoadableFile.FileType); | |
HandleOfSlicedArrayByte handleForResult = | |
PlatformAwareFileLoaderShared.BuilderOfSlicedArrayByte.BuildArray( | |
loadableFile.LoadableFile.UncompressedSize); | |
try | |
{ | |
using (FileStream file = File.OpenRead(fileName)) | |
{ | |
// (должны быть равны т.к. буфер выделяется строго под этот размер, и зачем выделять больший | |
// если убирается в меньший; ошибка консистентности размеров) | |
if (file.Length != loadableFile.LoadableFile.UncompressedSize) | |
{ | |
throw new Exception("PlatformAwareFileLoaderComplex::LoadSingleFromLocalAction: Actual length (" + file.Length + ") is not equals expected (" + loadableFile.LoadableFile.UncompressedSize + "), FileName = '" + fileName + "'"); | |
} | |
file.Read(handleForResult.TotalBlock, handleForResult.StartIndex, handleForResult.Length); | |
} | |
} | |
catch (FileNotFoundException) | |
{ | |
throw new Exception("PlatformAwareFileLoaderComplex::LoadSingleFromLocalAction: Not found FileName = '" + fileName + "'"); | |
} | |
return CreateResult(fileName, handleForResult); | |
} | |
/// <summary> | |
/// Загрузка файла из пакета для мобильных; для вызова в yield; строго для одиночной загрузки | |
/// </summary> | |
/// <param name="loadableFile">Информация о загружаемом файле</param> | |
/// <param name="result">Для сохранения результата</param> | |
private IEnumerator LoadSingleFromLocalMobilePack(TInput loadableFile, CoroutineResult<TResult> result) | |
{ | |
#if DEBUG_LOADER_LOGS | |
Debug.Log("LoadSingleFromLocalMobilePack"); | |
#endif | |
result.Value = LoadSingleFromLocalMobilePackAction(loadableFile); | |
// (ждем конца фрейма, получая замедление, но без yield синтаксически не пройдет функция) | |
yield return null; | |
} | |
/// <summary> | |
/// Реальная загрузка файла из пакета для мобильных; строго для одиночной загрузки | |
/// </summary> | |
/// <param name="loadableFile">Информация о загружаемом файле</param> | |
/// <returns>Результат</returns> | |
private TResult LoadSingleFromLocalMobilePackAction(TInput loadableFile) | |
{ | |
HandleOfSlicedArrayByte handleForResult = | |
PlatformAwareFileLoaderShared.MobileResourcePackLoader.ReadLoadableFile(loadableFile.LoadableFile); | |
return CreateResult(loadableFile.LoadableFile.ShortName, handleForResult); | |
} | |
/// <summary> | |
/// Создание результата загрузки одного файла | |
/// </summary> | |
/// <param name="fileName">Имя файла (возможно URL) для отладки</param> | |
/// <param name="handleForResult">Handle массива байт</param> | |
protected abstract TResult CreateResult(string fileName, HandleOfSlicedArrayByte handleForResult); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package pandemicaserver.Inventory; | |
import Assets.Network.Proto.ConsumableTypeOuterClass.ConsumableType; | |
import Assets.Network.Proto.UserInventoryOuterClass.AmmoEntry; | |
import Assets.Network.Proto.UserInventoryOuterClass.ConsumableEntry; | |
import Assets.Network.Proto.UserInventoryOuterClass.WeaponEntry; | |
import Assets.Network.Proto.WeaponTypeAndAbilityType.WeaponType; | |
import pandemicaserver.Utils.FuncUtils; | |
import java.util.*; | |
/** | |
* Инвентарь пользователя | |
*/ | |
public class UserInventory implements IUserInventoryModifiable, IUserInventoryReadOnly | |
{ | |
/** | |
* Карта расходных предметов: Тип -> Кол-во зарядов | |
*/ | |
private final Map<ConsumableType, Integer> Consumables = new HashMap<>(); | |
/** | |
* Карта оружий: Тип -> Кол-во патронов (-1 = бесконечное оружие) | |
*/ | |
private final Map<WeaponType, Integer> Weapons = new HashMap<>(); | |
/** | |
* Карта незаряженных патронов: Тип -> Кол-во патронов | |
* (когда нет подходящего оружия) | |
* @todo использовать позже | |
*/ | |
private final Map<WeaponType, Integer> Ammos = new HashMap<>(); | |
public UserInventory() | |
{ | |
} | |
/** | |
* Добавление (или уменьшение) расходных предметов | |
* @param type Тип | |
* @param add Кол-во добавленных зарядов (возможно отрицательное) | |
* @return Признак: Успешно (иначе - не достаточно зарядов) | |
*/ | |
@Override | |
public boolean AddConsumables(ConsumableType type, int add) | |
{ | |
return AddInMap(Consumables, type, add); | |
} | |
/** | |
* Добавление (или уменьшение) патронов к оружию | |
* @param type Тип | |
* @param add Кол-во патронов (возможно отрицательное) | |
* @return Признак: Успешно (иначе - не достаточно патронов) | |
*/ | |
@Override | |
public boolean AddWeaponAndAmmo(WeaponType type, int add) | |
{ | |
return AddInMap(Weapons, type, add); | |
} | |
/** | |
* Добавление (или уменьшение) элементов в карте | |
* @param <T> Класс элемента ключа (типа) | |
* @param map Карта для изменения | |
* @param key Ключ - Тип | |
* @param add Кол-во добавленных зарядов (возможно отрицательное) | |
* @return Признак: Успешно (иначе - не достаточно зарядов и не отняли нисколько) | |
*/ | |
private <T> boolean AddInMap(Map<T, Integer> map, T key, int add) | |
{ | |
int newCount = map.getOrDefault(key, 0) + add; | |
if (newCount < 0) return false; | |
if (newCount == 0) | |
{ | |
map.remove(key); | |
} | |
else | |
{ | |
map.put(key, newCount); | |
} | |
return true; | |
} | |
/** | |
* Добавление оружия с бесконечным числом патронов | |
* @param type Тип | |
*/ | |
@Override | |
public void AddWeaponUnlimited(WeaponType type) | |
{ | |
Weapons.put(type, -1); | |
} | |
/** | |
* Получение кол-ва расходных предметов | |
* @param type Тип | |
*/ | |
@Override | |
public int GetConsumablesCount(ConsumableType type) | |
{ | |
return Consumables.getOrDefault(type, 0); | |
} | |
/** | |
* Получение кол-ва патронов в оружии (в том числе отдельных без оружия) | |
* @param type Тип | |
*/ | |
@Override | |
public int GetWeaponAmmoCount(WeaponType type) | |
{ | |
return Weapons.getOrDefault(type, 0) + Ammos.getOrDefault(type, 0); | |
} | |
/** | |
* Перевод коллекции расходных предметов в список элементов protobuf | |
* @return | |
*/ | |
public List<ConsumableEntry> ConsumablesToProtoList() | |
{ | |
return FuncUtils.ConvertMapToList(Consumables, | |
(key, value) -> ConsumableEntry.newBuilder() | |
.setType(key) | |
.setCount(value) | |
.build() | |
); | |
} | |
/** | |
* Перевод коллекции оружий в список элементов protobuf | |
* @return | |
*/ | |
public List<WeaponEntry> WeaponsToProtoList() | |
{ | |
return FuncUtils.ConvertMapToList(Weapons, | |
(key, value) -> WeaponEntry.newBuilder() | |
.setType(key) | |
.setCount(value) | |
.build() | |
); | |
} | |
/** | |
* Перевод коллекции незаряженных патронов в список элементов protobuf | |
* @return | |
*/ | |
public List<AmmoEntry> AmmosToProtoList() | |
{ | |
return FuncUtils.ConvertMapToList(Ammos, | |
(key, value) -> AmmoEntry.newBuilder() | |
.setType(key) | |
.setCount(value) | |
.build() | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment