Немного поменял концепцию. Во первых убрал модификатор predictable, поскольку нет нужды это делать выборочно. Всё что попало на клиент нуждается в предиктинге, если включен соответствующий квар.
Т.е. там можно сделать некоторые режимы, типа предиктить всё, предиктить только игроков, а внутри VM это будет только запутывать. Поскольку нам надо предсказывать поведение объектов, а не отдельных переменных.
Вместо этого ввёл модификатор unsigned, который влияет только на сейв-рестор и передачу по сети. Так же важный момент, после объявления переменной через двоеточие объявляется сколько бит использует эта переменная. В VM эта настройка не будет влиять на непосредственно размер переменной (по крайней мере на текущий момент). Вместо этого она будет использоваться как хинт - сколько бит используется в данной переменной для сохранения в сейв и передачи на клиент по сети. Т.е. такой очень удобный и наглядный аналог delta.lst. Безо всяких идиотских таблиц и прочего. Кол-во бит будет работать и на флоатах тоже. Никаких множителей не понадобится, этот способ гораздо точнее и надёжнее. Т.е. по сети можно будет передавать даже восьмибитные флоаты или что-то вроде этого.
Ну а в дальнейшем возможно и реальный размер переменных тоже подгонится под заданный, но я, если честно не вижу в этом особого смысла.
При передаче по сети - очень даже при чём! Я больше скажу - в обычной программе народ мешает signed\unsigned и это почти никогда ни к каким проблемам не приводит. Всё дерьмо начинается как раз при передаче по сети.
Ты просто не сталкивался.
Добавлено 19-04-2022 в 12:41:
Собсно, фишка в чём - в знаковой переменной вполне может лежать беззнаковый тип. А это напрямую влияет как будет использован один из битов, заданных для определения размера при передаче по сети.
На мой взгляд, две самые стрёмные операции, которые абсолютно не нужны в скриптовом языке - это взятие адреса и разыменование указателей.
Не берусь предположить, можно ли было без них обойтись в С++, но ведь он вырос из Си, значит следовало обеспечить полную совместимость.
Но поскольку в С++ есть ссылки, потребность во взятии адреса, на мой взгляд отпадает полностью. Обычно адрес берут, чтобы передать в функцию указатель на аргумент, который будет модифицироваться. Ссылка делает тоже самое, только безопасным образом.
Тут идея в чём. Если указатель не был получен легальным образом, значит он использует какие-то объекты, на которые он не нужен. Глобальные или стековые. Здесь ссылка прекрасно годится.
Но конечно я не исключаю, что не учёл чего-то, по мере развития языка будет видно.
Дядя Миша писал: На мой взгляд, две самые стрёмные операции, которые абсолютно не нужны в скриптовом языке - это взятие адреса и разыменование указателей.
А я думал, одна из самых важных особенностей скриптовых языков и виртуальных машин в том, чтобы держать кодера подальше от прямого доступа к памяти. Или я неправильно понимаю?
Делаю свитч-кейсы и встал интересный вопрос. Вот конструкция:
C++ Source Code:
1
void test( int i )
2
{
3
switch( i )
4
{
5
case '1':
6
break;
7
case 2:
8
break;
9
case"foo":
10
break;
11
}
12
}
Шестёрка на последний кейс почему-то ругается case expression not constant. Что за бред? А какая же это строка? Не константная штоле?
Она должна ругаться case expression of type 'const char' is illegal
или что-то вроде этого. Проверьте как ругаются другие компиляторы.
Дядя Миша писал: А какая же это строка? Не константная штоле?
Это не строка, а указатель на некую область памяти, которая будет доступна только при старте программы. В плюсах нет неко ких константных строк, ты при желании можешь её изменить в рантайме.
В делфях оказывается есть указатель на функцию абстрактного класса.
Ну т.е. этот тайпдеф необязательно объявлять внутри класса, можно в глобальном пространстве, а вместо имени класса написать object.
Я ради прикола попробовал объявить нечто подобное в С++
C++ Source Code:
void (class::*foo)( void );
В шестерке сразу получилась INTERNAL_COMPILER_ERROR.
Попробуйте на других компиляторах
Дошло. Тут неинтуитивное поведение на самом деле.
Если мы пишем
C++ Source Code:
int foo[8] = { 0 };
У нас массив заполняется нулями. После чего мы вправе(?) ожидать что вот такая конструкция
C++ Source Code:
int foo[8] = { -1 };
Запишет везде -1, однако этого естественно не происходит.
Сама операция присовоения вызывает установку всех элементов массива в 0, кроме тех, для которых значения указаны явно. В первом случае явно указано значение для первого элемента, оно равно нулю, остальные равны нулю по дефолту, из-за чего и формируется ложный стереотип, будто задание еденичного значения забьёт все элементы этим числом. Но в реальности этого конечно не происходит. Аналогичная фигня, кстати и с мемсетом.
Мы задаем ему -1 и ждём, что будет забивать массив наших значений этим числом. Хотя по факту он работает с байтами, т.е. каждый байт 32-битного числа будет забит -1. Что опять-таки явно не то поведение, которое нам бы требовалось. Но в подавляющем большинстве случаев мемсет используется чтобы обнулить память, и это проходит незамеченным. Вполне вероятно, что сам мемсет не стали трогать ради совместимости, сделали еще функцию ZeroMemory, а так же вероятно есть какой-то продвинутый std::memset (но я просто предполагаю), который забивает память вот как раз с учётом типа входного массива.
Дядя Миша писал: После чего мы вправе(?) ожидать что вот такая конструкция
Это очень странная логика: считать, что если инициализатор не указан, то используется значение предыдущего. Она очень человеческая, но не математическая.
С точки зрения математической логики, кмк, стоит ожидать, что если инициализатор не указан - то используется 0. Ну сам посуди, когда мы видим запись десятичной дроби: 1.2, что логичнее предположить, что она на самом деле "1.222222222222222222..." или "1.2000000000000000..."?