diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java index 000685011..6b1ccc4d3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java @@ -26,10 +26,12 @@ import com.seibel.distanthorizons.core.generation.DhLightingEngine; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.f3.F3Screen; +import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhChunkPos; +import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; -import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; import com.seibel.distanthorizons.core.util.TimerUtil; import com.seibel.distanthorizons.core.util.objects.Pair; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; @@ -345,6 +347,12 @@ public class SharedApi DhLightingEngine.INSTANCE.lightChunk(chunkWrapper, nearbyChunkList, dhLevel.hasSkyLight() ? 15 : 0); } + + // get this chunk's active beacons + List beaconBeamList = chunkWrapper.getAllActiveBeacons(); + dhLevel.ensureBeaconBeamsAtPos(DhSectionPos.encode(chunkWrapper.getChunkPos()), beaconBeamList); + + dhLevel.updateChunkAsync(chunkWrapper); dhLevel.setChunkHash(chunkWrapper.getChunkPos(), newChunkHash); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java index 6cb44e31e..6831cb403 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java @@ -19,14 +19,21 @@ package com.seibel.distanthorizons.core.level; +import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiChunkModifiedEvent; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.dataObjects.transformers.ChunkToLodBuilder; import com.seibel.distanthorizons.core.file.fullDatafile.DelayedFullDataSourceSaveCache; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.render.renderer.GenericObjectRenderer; +import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; import com.seibel.distanthorizons.core.sql.dto.ChunkHashDTO; +import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo; import com.seibel.distanthorizons.core.sql.repo.ChunkHashRepo; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; @@ -35,7 +42,10 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; public abstract class AbstractDhLevel implements IDhLevel @@ -47,6 +57,9 @@ public abstract class AbstractDhLevel implements IDhLevel /** if this is null then the other handler is probably null too, but just in case */ @Nullable public ChunkHashRepo chunkHashRepo; + /** if this is null then the other handler is probably null too, but just in case */ + @Nullable + public BeaconBeamRepo beaconBeamRepo; protected final DelayedFullDataSourceSaveCache delayedFullDataSourceSaveCache = new DelayedFullDataSourceSaveCache(this::onDataSourceSave, 2_000); /** contains the {@link DhChunkPos} for each {@link DhSectionPos} that are queued to save via {@link AbstractDhLevel#delayedFullDataSourceSaveCache} */ @@ -60,8 +73,9 @@ public abstract class AbstractDhLevel implements IDhLevel protected AbstractDhLevel() { this.chunkToLodBuilder = new ChunkToLodBuilder(); } - protected void createAndSetChunkHashRepo(File databaseFile) + protected void createAndSetSupportingRepos(File databaseFile) { + // chunk hash ChunkHashRepo newChunkHashRepo = null; try { @@ -72,6 +86,19 @@ public abstract class AbstractDhLevel implements IDhLevel LOGGER.error("Unable to create [ChunkHashRepo], error: ["+e.getMessage()+"].", e); } this.chunkHashRepo = newChunkHashRepo; + + + // beacon beam + BeaconBeamRepo newBeaconBeamRepo = null; + try + { + newBeaconBeamRepo = new BeaconBeamRepo("jdbc:sqlite", databaseFile); + } + catch (SQLException e) + { + LOGGER.error("Unable to create [BeaconBeamRepo], error: ["+e.getMessage()+"].", e); + } + this.beaconBeamRepo = newBeaconBeamRepo; } @@ -126,6 +153,13 @@ public abstract class AbstractDhLevel implements IDhLevel } + + //=======// + // repos // + //=======// + + // chunk hash // + @Override public int getChunkHash(DhChunkPos pos) { @@ -148,6 +182,92 @@ public abstract class AbstractDhLevel implements IDhLevel + // beacon beam // + + @Override + public List getAllBeamsForSectionPos(long pos) + { + if (this.beaconBeamRepo != null) + { + return this.beaconBeamRepo.getAllBeamsForSectionPos(pos); + } + else + { + return new ArrayList<>(0); + } + } + + + HashMap beamRenderGroupByPos = new HashMap<>(); + + @Override + public void ensureBeaconBeamsAtPos(long pos, List newBeamList) + { + if (this.beaconBeamRepo != null) + { + HashSet allPosSet = new HashSet<>(); + + // sort new beams + HashMap newBeamByPos = new HashMap<>(newBeamList.size()); + for (int i = 0; i < newBeamList.size(); i++) + { + BeaconBeamDTO beam = newBeamList.get(i); + newBeamByPos.put(beam.pos, beam); + allPosSet.add(beam.pos); + } + + // get existing beams + List existingBeamList = this.beaconBeamRepo.getAllBeamsForSectionPos(pos); + HashMap existingBeamByPos = new HashMap<>(existingBeamList.size()); + for (int i = 0; i < existingBeamList.size(); i++) + { + BeaconBeamDTO beam = existingBeamList.get(i); + existingBeamByPos.put(beam.pos, beam); + allPosSet.add(beam.pos); + } + + + + for (DhBlockPos beaconPos : allPosSet) + { + BeaconBeamDTO existingBeam = existingBeamByPos.get(beaconPos); + BeaconBeamDTO newBeam = newBeamByPos.get(beaconPos); + + if (existingBeam != null && newBeam != null) + { + // beam still exists in chunk, do nothing + } + else if (existingBeam == null && newBeam != null) + { + // new beam found, add to DB + this.beaconBeamRepo.save(newBeam); + + IDhApiRenderableBoxGroup beaconBox = GenericObjectRenderer.INSTANCE.createForSingleBox(new DhApiRenderableBox( + new DhApiVec3f(newBeam.pos.x, newBeam.pos.y+1, newBeam.pos.z), + new DhApiVec3f(newBeam.pos.x+1, 2_000, newBeam.pos.z+1), + newBeam.color + )); + beamRenderGroupByPos.put(newBeam.pos, beaconBox); + GenericObjectRenderer.INSTANCE.add(beaconBox); + } + else if (existingBeam != null && newBeam == null) + { + // beam no longer exists at position, remove + this.beaconBeamRepo.deleteWithKey(beaconPos); + IDhApiRenderableBoxGroup beaconBox = beamRenderGroupByPos.remove(newBeam.pos); + if (beaconBox != null) + { + GenericObjectRenderer.INSTANCE.remove(beaconBox.getId()); + } + } + + } + + } + } + + + //================// // base overrides // //================// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index f3fa99055..f54db8781 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -62,7 +62,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel this.dataFileHandler = new RemoteFullDataSourceProvider(this, saveStructure, fullDataSaveDirOverride); this.clientside = new ClientLevelModule(this); - this.createAndSetChunkHashRepo(this.dataFileHandler.repo.databaseFile); + this.createAndSetSupportingRepos(this.dataFileHandler.repo.databaseFile); if (enableRendering) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java index 5a210bf08..c961240ed 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java @@ -68,7 +68,7 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev this.serverLevelWrapper = serverLevelWrapper; this.serverside = new ServerLevelModule(this, saveStructure); this.clientside = new ClientLevelModule(this); - this.createAndSetChunkHashRepo(this.serverside.fullDataFileHandler.repo.databaseFile); + this.createAndSetSupportingRepos(this.serverside.fullDataFileHandler.repo.databaseFile); LOGGER.info("Started " + DhClientServerLevel.class.getSimpleName() + " for " + serverLevelWrapper + " with saves at " + saveStructure); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 13a520ff8..c14eb617b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -47,7 +47,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } this.serverLevelWrapper = serverLevelWrapper; this.serverside = new ServerLevelModule(this, saveStructure); - this.createAndSetChunkHashRepo(this.serverside.fullDataFileHandler.repo.databaseFile); + this.createAndSetSupportingRepos(this.serverside.fullDataFileHandler.repo.databaseFile); LOGGER.info("Started DHLevel for {} with saves at {}", serverLevelWrapper, saveStructure); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java index 66e32c3da..5a52cd29a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java @@ -22,7 +22,9 @@ package com.seibel.distanthorizons.core.level; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; +import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhChunkPos; +import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; @@ -44,6 +46,9 @@ public interface IDhLevel extends AutoCloseable void setChunkHash(DhChunkPos pos, int chunkHash); void updateChunkAsync(IChunkWrapper chunk); + List getAllBeamsForSectionPos(long pos); + void ensureBeaconBeamsAtPos(long pos, List beamList); + FullDataSourceProviderV2 getFullDataProvider(); AbstractSaveStructure getSaveStructure(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/GenericObjectRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/GenericObjectRenderer.java index 7c5b3bf70..39983e77e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/GenericObjectRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/GenericObjectRenderer.java @@ -74,6 +74,7 @@ import java.util.stream.Stream; */ public class GenericObjectRenderer implements IDhApiCustomRenderRegister { + @Deprecated public static GenericObjectRenderer INSTANCE = new GenericObjectRenderer(); private static final Logger LOGGER = DhLoggerBuilder.getLogger(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/BeaconBeamDTO.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/BeaconBeamDTO.java new file mode 100644 index 000000000..452af3873 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/BeaconBeamDTO.java @@ -0,0 +1,55 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.sql.dto; + +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; +import com.seibel.distanthorizons.core.pos.DhBlockPos; +import com.seibel.distanthorizons.core.pos.DhBlockPos; + +import java.awt.*; + +/** handles storing {@link FullDataSourceV2}'s in the database. */ +public class BeaconBeamDTO implements IBaseDTO +{ + public DhBlockPos pos; + public Color color; + + + + //=============// + // constructor // + //=============// + + public BeaconBeamDTO(DhBlockPos pos, Color color) + { + this.pos = pos; + this.color = color; + } + + + + //===========// + // overrides // + //===========// + + @Override + public DhBlockPos getKey() { return this.pos; } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/BeaconBeamRepo.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/BeaconBeamRepo.java new file mode 100644 index 000000000..5c9767bea --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/BeaconBeamRepo.java @@ -0,0 +1,152 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.sql.repo; + +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.pos.DhBlockPos; +import com.seibel.distanthorizons.core.pos.DhBlockPos; +import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; +import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; +import org.apache.logging.log4j.Logger; + +import java.awt.*; +import java.io.File; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +public class BeaconBeamRepo extends AbstractDhRepo +{ + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + + + + //=============// + // constructor // + //=============// + + public BeaconBeamRepo(String databaseType, File databaseFile) throws SQLException + { + super(databaseType, databaseFile, BeaconBeamDTO.class); + } + + + + //===========// + // overrides // + //===========// + + @Override + public String getTableName() { return "BeaconBeam"; } + + @Override + public String createWhereStatement(DhBlockPos pos) { return "BlockPosX = "+pos.x+" AND BlockPosY = "+pos.y+" AND BlockPosZ = "+pos.z; } + + + + //=======================// + // repo required methods // + //=======================// + + @Override + public BeaconBeamDTO convertDictionaryToDto(Map objectMap) throws ClassCastException + { + int posX = (Integer) objectMap.get("BlockPosX"); + int posY = (Integer) objectMap.get("BlockPosY"); + int posZ = (Integer) objectMap.get("BlockPosZ"); + + int red = (Integer) objectMap.get("ColorR"); + int green = (Integer) objectMap.get("ColorG"); + int blue = (Integer) objectMap.get("ColorB"); + + + BeaconBeamDTO dto = new BeaconBeamDTO(new DhBlockPos(posX, posY, posZ), new Color(red, green, blue)); + return dto; + } + + @Override + public PreparedStatement createInsertStatement(BeaconBeamDTO dto) throws SQLException + { + String sql = + "INSERT INTO "+this.getTableName() + " (\n" + + " BlockPosX, BlockPosY, BlockPosZ, \n" + + " ColorR, ColorG, ColorB, \n" + + " LastModifiedUnixDateTime, CreatedUnixDateTime) \n" + + "VALUES( \n" + + " ?, ?, ?, \n" + + " ?, ?, ?, \n" + + " ?, ? \n" + + ");"; + PreparedStatement statement = this.createPreparedStatement(sql); + + int i = 1; + statement.setObject(i++, dto.pos.x); + statement.setObject(i++, dto.pos.y); + statement.setObject(i++, dto.pos.z); + + statement.setObject(i++, dto.color.getRed()); + statement.setObject(i++, dto.color.getGreen()); + statement.setObject(i++, dto.color.getBlue()); + + statement.setObject(i++, System.currentTimeMillis()); // last modified unix time + statement.setObject(i++, System.currentTimeMillis()); // created unix time + + return statement; + } + + @Override + public PreparedStatement createUpdateStatement(BeaconBeamDTO dto) throws SQLException + { + String sql = + "UPDATE "+this.getTableName()+" \n" + + "SET \n" + + " ColorR = ?, ColorG = ?, ColorB = ?, \n" + + " ,LastModifiedUnixDateTime = ? \n" + + "WHERE BlockPosX = ? AND BlockPosY = ? AND BlockPosZ = ?"; + PreparedStatement statement = this.createPreparedStatement(sql); + + int i = 1; + statement.setObject(i++, dto.color.getRed()); + statement.setObject(i++, dto.color.getGreen()); + statement.setObject(i++, dto.color.getBlue()); + + statement.setObject(i++, System.currentTimeMillis()); // last modified unix time + + statement.setObject(i++, dto.pos.x); + statement.setObject(i++, dto.pos.y); + statement.setObject(i++, dto.pos.z); + + return statement; + } + + + + //====================// + // additional methods // + //====================// + + public List getAllBeamsForSectionPos(long pos) + { + return null; + } + + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/IChunkWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/IChunkWrapper.java index d63633f43..ca79eb837 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/IChunkWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/IChunkWrapper.java @@ -22,13 +22,16 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.chunk; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhChunkPos; +import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; +import java.awt.*; import java.util.ArrayList; +import java.util.List; public interface IChunkWrapper extends IBindable { @@ -243,5 +246,24 @@ public interface IChunkWrapper extends IBindable return hash; } + default List getAllActiveBeacons() + { + ArrayList beaconPosList = new ArrayList<>(); + + // since beacons emit light we can check only the positions that are emitting light + ArrayList blockPosList = this.getBlockLightPosList(); + for (int i = 0; i < blockPosList.size(); i++) + { + DhBlockPos pos = blockPosList.get(i).convertToChunkRelativePos(); + IBlockStateWrapper block = this.getBlockState(pos); + if (block.getSerialString().equalsIgnoreCase("minecraft:beacon")) + { + BeaconBeamDTO beam = new BeaconBeamDTO(pos, Color.WHITE); // TODO + beaconPosList.add(beam); + } + } + + return beaconPosList; + } } diff --git a/core/src/main/resources/sqlScripts/0070-sqlite-createBeaconBeamTable.sql b/core/src/main/resources/sqlScripts/0070-sqlite-createBeaconBeamTable.sql new file mode 100644 index 000000000..48a5040a6 --- /dev/null +++ b/core/src/main/resources/sqlScripts/0070-sqlite-createBeaconBeamTable.sql @@ -0,0 +1,16 @@ + +CREATE TABLE BeaconBeam( + -- compound primary key + BlockPosX INT NOT NULL + ,BlockPosY INT NOT NULL + ,BlockPosZ INT NOT NULL + + ,ColorR INT NOT NULL + ,ColorG INT NOT NULL + ,ColorB INT NOT NULL + + ,LastModifiedUnixDateTime BIGINT NOT NULL -- in GMT 0 + ,CreatedUnixDateTime BIGINT NOT NULL -- in GMT 0 + + ,PRIMARY KEY (BlockPosX, BlockPosY, BlockPosZ) +); diff --git a/core/src/main/resources/sqlScripts/scriptList.txt b/core/src/main/resources/sqlScripts/scriptList.txt index 52027a061..7c6ed02ea 100644 --- a/core/src/main/resources/sqlScripts/scriptList.txt +++ b/core/src/main/resources/sqlScripts/scriptList.txt @@ -6,3 +6,4 @@ 0040-sqlite-removeRenderCache.sql 0050-sqlite-addApplyToParentIndex.sql 0060-sqlite-createChunkHashTable.sql +0070-sqlite-createBeaconBeamTable.sql